zig

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

blob de5d202b (1607353B) - 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 const std = @import("std");
      9 const math = std.math;
     10 const mem = std.mem;
     11 const Allocator = mem.Allocator;
     12 const assert = std.debug.assert;
     13 const log = std.log.scoped(.sema);
     14 
     15 const Sema = @This();
     16 const Value = @import("Value.zig");
     17 const MutableValue = @import("mutable_value.zig").MutableValue;
     18 const Type = @import("Type.zig");
     19 const Air = @import("Air.zig");
     20 const Zir = std.zig.Zir;
     21 const Zcu = @import("Zcu.zig");
     22 const trace = @import("tracy.zig").trace;
     23 const Namespace = Zcu.Namespace;
     24 const CompileError = Zcu.CompileError;
     25 const SemaError = Zcu.SemaError;
     26 const LazySrcLoc = Zcu.LazySrcLoc;
     27 const RangeSet = @import("RangeSet.zig");
     28 const target_util = @import("target.zig");
     29 const Package = @import("Package.zig");
     30 const crash_report = @import("crash_report.zig");
     31 const build_options = @import("build_options");
     32 const Compilation = @import("Compilation.zig");
     33 const InternPool = @import("InternPool.zig");
     34 const Alignment = InternPool.Alignment;
     35 const AnalUnit = InternPool.AnalUnit;
     36 const ComptimeAllocIndex = InternPool.ComptimeAllocIndex;
     37 const Cache = std.Build.Cache;
     38 const LowerZon = @import("Sema/LowerZon.zig");
     39 const arith = @import("Sema/arith.zig");
     40 
     41 pt: Zcu.PerThread,
     42 /// Alias to `zcu.gpa`.
     43 gpa: Allocator,
     44 /// Points to the temporary arena allocator of the Sema.
     45 /// This arena will be cleared when the sema is destroyed.
     46 arena: Allocator,
     47 code: Zir,
     48 air_instructions: std.MultiArrayList(Air.Inst) = .{},
     49 air_extra: std.ArrayListUnmanaged(u32) = .empty,
     50 /// Maps ZIR to AIR.
     51 inst_map: InstMap = .{},
     52 /// The "owner" of a `Sema` represents the root "thing" that is being analyzed.
     53 /// This does not change throughout the entire lifetime of a `Sema`. For instance,
     54 /// when analyzing a runtime function body, this is always `func` of that function,
     55 /// even if an inline/comptime function call is being analyzed.
     56 owner: AnalUnit,
     57 /// The function this ZIR code is the body of, according to the source code.
     58 /// This starts out the same as `sema.owner.func` if applicable, and then diverges
     59 /// in the case of an inline or comptime function call.
     60 /// This could be `none`, a `func_decl`, or a `func_instance`.
     61 func_index: InternPool.Index,
     62 /// Whether the type of func_index has a calling convention of `.naked`.
     63 func_is_naked: bool,
     64 /// Used to restore the error return trace when returning a non-error from a function.
     65 error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none,
     66 comptime_err_ret_trace: *std.array_list.Managed(LazySrcLoc),
     67 /// When semantic analysis needs to know the return type of the function whose body
     68 /// is being analyzed, this `Type` should be used instead of going through `func`.
     69 /// This will correctly handle the case of a comptime/inline function call of a
     70 /// generic function which uses a type expression for the return type.
     71 /// The type will be `void` in the case that `func` is `null`.
     72 fn_ret_ty: Type,
     73 /// In case of the return type being an error union with an inferred error
     74 /// set, this is the inferred error set. `null` otherwise. Allocated with
     75 /// `Sema.arena`.
     76 fn_ret_ty_ies: ?*InferredErrorSet,
     77 branch_quota: u32 = default_branch_quota,
     78 branch_count: u32 = 0,
     79 /// Populated when returning `error.ComptimeBreak`. Used to communicate the
     80 /// break instruction up the stack to find the corresponding Block.
     81 comptime_break_inst: Zir.Inst.Index = undefined,
     82 /// These are lazily created runtime blocks from block_inline instructions.
     83 /// They are created when an break_inline passes through a runtime condition, because
     84 /// Sema must convert comptime control flow to runtime control flow, which means
     85 /// breaking from a block.
     86 post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .empty,
     87 /// Populated with the last compile error created.
     88 err: ?*Zcu.ErrorMsg = null,
     89 
     90 /// The temporary arena is used for the memory of the `InferredAlloc` values
     91 /// here so the values can be dropped without any cleanup.
     92 unresolved_inferred_allocs: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InferredAlloc) = .empty,
     93 
     94 /// Links every pointer derived from a base `alloc` back to that `alloc`. Used
     95 /// to detect comptime-known `const`s.
     96 /// TODO: ZIR liveness analysis would allow us to remove elements from this map.
     97 base_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, Air.Inst.Index) = .empty,
     98 
     99 /// Runtime `alloc`s are placed in this map to track all comptime-known writes
    100 /// before the corresponding `make_ptr_const` instruction.
    101 /// If any store to the alloc depends on a runtime condition or stores a runtime
    102 /// value, the corresponding element in this map is erased, to indicate that the
    103 /// alloc is not comptime-known.
    104 /// If the alloc remains in this map when `make_ptr_const` is reached, its value
    105 /// is comptime-known, and all stores to the pointer must be applied at comptime
    106 /// to determine the comptime value.
    107 /// Backed by gpa.
    108 maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAlloc) = .empty,
    109 
    110 /// Comptime-mutable allocs, and any comptime allocs which reference it, are
    111 /// stored as elements of this array.
    112 /// Pointers to such memory are represented via an index into this array.
    113 /// Backed by gpa.
    114 comptime_allocs: std.ArrayListUnmanaged(ComptimeAlloc) = .empty,
    115 
    116 /// A list of exports performed by this analysis. After this `Sema` terminates,
    117 /// these are flushed to `Zcu.single_exports` or `Zcu.multi_exports`.
    118 exports: std.ArrayListUnmanaged(Zcu.Export) = .empty,
    119 
    120 /// All references registered so far by this `Sema`. This is a temporary duplicate
    121 /// of data stored in `Zcu.all_references`. It exists to avoid adding references to
    122 /// a given `AnalUnit` multiple times.
    123 references: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .empty,
    124 type_references: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty,
    125 
    126 /// All dependencies registered so far by this `Sema`. This is a temporary duplicate
    127 /// of the main dependency data. It exists to avoid adding dependencies to a given
    128 /// `AnalUnit` multiple times.
    129 dependencies: std.AutoArrayHashMapUnmanaged(InternPool.Dependee, void) = .empty,
    130 
    131 /// Whether memoization of this call is permitted. Operations with side effects global
    132 /// to the `Sema`, such as `@setEvalBranchQuota`, set this to `false`. It is observed
    133 /// by `analyzeCall`.
    134 allow_memoize: bool = true,
    135 
    136 /// The `BranchHint` for the current branch of runtime control flow.
    137 /// This state is on `Sema` so that `cold` hints can be propagated up through blocks with less special handling.
    138 branch_hint: ?std.builtin.BranchHint = null,
    139 
    140 const RuntimeIndex = enum(u32) {
    141     zero = 0,
    142     comptime_field_ptr = std.math.maxInt(u32),
    143     _,
    144 
    145     pub fn increment(ri: *RuntimeIndex) void {
    146         ri.* = @enumFromInt(@intFromEnum(ri.*) + 1);
    147     }
    148 };
    149 
    150 const MaybeComptimeAlloc = struct {
    151     /// The runtime index of the `alloc` instruction.
    152     runtime_index: RuntimeIndex,
    153     /// Backed by sema.arena. Tracks all comptime-known stores to this `alloc`. Due to
    154     /// RLS, a single comptime-known allocation may have arbitrarily many stores.
    155     /// This list also contains `set_union_tag`, `optional_payload_ptr_set`, and
    156     /// `errunion_payload_ptr_set` instructions.
    157     /// If the instruction is one of these three tags, `src` may be `.unneeded`.
    158     stores: std.MultiArrayList(struct {
    159         inst: Air.Inst.Index,
    160         src: LazySrcLoc,
    161     }) = .{},
    162 };
    163 
    164 const ComptimeAlloc = struct {
    165     val: MutableValue,
    166     is_const: bool,
    167     src: LazySrcLoc,
    168     /// `.none` indicates that the alignment is the natural alignment of `val`.
    169     alignment: Alignment,
    170     /// This is the `runtime_index` at the point of this allocation. If an store
    171     /// to this alloc ever occurs with a runtime index greater than this one, it
    172     /// is behind a runtime condition, so a compile error will be emitted.
    173     runtime_index: RuntimeIndex,
    174 };
    175 
    176 /// `src` may be `null` if `is_const` will be set.
    177 fn newComptimeAlloc(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, alignment: Alignment) !ComptimeAllocIndex {
    178     const idx = sema.comptime_allocs.items.len;
    179     try sema.comptime_allocs.append(sema.gpa, .{
    180         .val = .{ .interned = try sema.pt.intern(.{ .undef = ty.toIntern() }) },
    181         .is_const = false,
    182         .src = src,
    183         .alignment = alignment,
    184         .runtime_index = block.runtime_index,
    185     });
    186     return @enumFromInt(idx);
    187 }
    188 
    189 pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc {
    190     return &sema.comptime_allocs.items[@intFromEnum(idx)];
    191 }
    192 
    193 pub const default_branch_quota = 1000;
    194 
    195 pub const InferredErrorSet = struct {
    196     /// The function body from which this error set originates.
    197     /// This is `none` in the case of a comptime/inline function call, corresponding to
    198     /// `InternPool.Index.adhoc_inferred_error_set_type`.
    199     /// The function's resolved error set is not set until analysis of the
    200     /// function body completes.
    201     func: InternPool.Index,
    202     /// All currently known errors that this error set contains. This includes
    203     /// direct additions via `return error.Foo;`, and possibly also errors that
    204     /// are returned from any dependent functions.
    205     errors: NameMap = .{},
    206     /// Other inferred error sets which this inferred error set should include.
    207     inferred_error_sets: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty,
    208     /// The regular error set created by resolving this inferred error set.
    209     resolved: InternPool.Index = .none,
    210 
    211     pub const NameMap = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void);
    212 
    213     pub fn addErrorSet(
    214         self: *InferredErrorSet,
    215         err_set_ty: Type,
    216         ip: *InternPool,
    217         arena: Allocator,
    218     ) !void {
    219         switch (err_set_ty.toIntern()) {
    220             .anyerror_type => self.resolved = .anyerror_type,
    221             .adhoc_inferred_error_set_type => {}, // Adding an inferred error set to itself.
    222 
    223             else => switch (ip.indexToKey(err_set_ty.toIntern())) {
    224                 .error_set_type => |error_set_type| {
    225                     for (error_set_type.names.get(ip)) |name| {
    226                         try self.errors.put(arena, name, {});
    227                     }
    228                 },
    229                 .inferred_error_set_type => {
    230                     try self.inferred_error_sets.put(arena, err_set_ty.toIntern(), {});
    231                 },
    232                 else => unreachable,
    233             },
    234         }
    235     }
    236 };
    237 
    238 /// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve
    239 /// instructions during analysis.
    240 /// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the
    241 /// zir instruction index and a start offset. An index is not present in the map if the value
    242 /// at the index is `Air.Inst.Ref.none`.
    243 /// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that
    244 /// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can
    245 /// be called safely for any of the instructions passed in.
    246 pub const InstMap = struct {
    247     items: []Air.Inst.Ref = &[_]Air.Inst.Ref{},
    248     start: Zir.Inst.Index = @enumFromInt(0),
    249 
    250     pub fn deinit(map: InstMap, allocator: mem.Allocator) void {
    251         allocator.free(map.items);
    252     }
    253 
    254     pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref {
    255         if (!map.contains(key)) return null;
    256         return map.items[@intFromEnum(key) - @intFromEnum(map.start)];
    257     }
    258 
    259     pub fn putAssumeCapacity(
    260         map: *InstMap,
    261         key: Zir.Inst.Index,
    262         ref: Air.Inst.Ref,
    263     ) void {
    264         map.items[@intFromEnum(key) - @intFromEnum(map.start)] = ref;
    265     }
    266 
    267     pub fn putAssumeCapacityNoClobber(
    268         map: *InstMap,
    269         key: Zir.Inst.Index,
    270         ref: Air.Inst.Ref,
    271     ) void {
    272         assert(!map.contains(key));
    273         map.putAssumeCapacity(key, ref);
    274     }
    275 
    276     pub const GetOrPutResult = struct {
    277         value_ptr: *Air.Inst.Ref,
    278         found_existing: bool,
    279     };
    280 
    281     pub fn getOrPutAssumeCapacity(
    282         map: *InstMap,
    283         key: Zir.Inst.Index,
    284     ) GetOrPutResult {
    285         const index = @intFromEnum(key) - @intFromEnum(map.start);
    286         return GetOrPutResult{
    287             .value_ptr = &map.items[index],
    288             .found_existing = map.items[index] != .none,
    289         };
    290     }
    291 
    292     pub fn remove(map: InstMap, key: Zir.Inst.Index) bool {
    293         if (!map.contains(key)) return false;
    294         map.items[@intFromEnum(key) - @intFromEnum(map.start)] = .none;
    295         return true;
    296     }
    297 
    298     pub fn contains(map: InstMap, key: Zir.Inst.Index) bool {
    299         return map.items[@intFromEnum(key) - @intFromEnum(map.start)] != .none;
    300     }
    301 
    302     pub fn ensureSpaceForInstructions(
    303         map: *InstMap,
    304         allocator: mem.Allocator,
    305         insts: []const Zir.Inst.Index,
    306     ) !void {
    307         const start, const end = mem.minMax(u32, @ptrCast(insts));
    308         const map_start = @intFromEnum(map.start);
    309         if (map_start <= start and end < map.items.len + map_start)
    310             return;
    311 
    312         const old_start = if (map.items.len == 0) start else map_start;
    313         var better_capacity = map.items.len;
    314         var better_start = old_start;
    315         while (true) {
    316             const extra_capacity = better_capacity / 2 + 16;
    317             better_capacity += extra_capacity;
    318             better_start -|= @intCast(extra_capacity / 2);
    319             if (better_start <= start and end < better_capacity + better_start)
    320                 break;
    321         }
    322 
    323         const start_diff = old_start - better_start;
    324         const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity);
    325         @memset(new_items[0..start_diff], .none);
    326         @memcpy(new_items[start_diff..][0..map.items.len], map.items);
    327         @memset(new_items[start_diff + map.items.len ..], .none);
    328 
    329         allocator.free(map.items);
    330         map.items = new_items;
    331         map.start = @enumFromInt(better_start);
    332     }
    333 };
    334 
    335 /// This is the context needed to semantically analyze ZIR instructions and
    336 /// produce AIR instructions.
    337 /// This is a temporary structure stored on the stack; references to it are valid only
    338 /// during semantic analysis of the block.
    339 pub const Block = struct {
    340     parent: ?*Block,
    341     /// Shared among all child blocks.
    342     sema: *Sema,
    343     /// The namespace to use for lookups from this source block
    344     namespace: InternPool.NamespaceIndex,
    345     /// The AIR instructions generated for this block.
    346     instructions: std.ArrayListUnmanaged(Air.Inst.Index),
    347     // `param` instructions are collected here to be used by the `func` instruction.
    348     /// When doing a generic function instantiation, this array collects a type
    349     /// for each *runtime-known* parameter. This array corresponds to the instance
    350     /// function type, while `Sema.comptime_args` corresponds to the generic owner
    351     /// function type.
    352     /// This memory is allocated by a parent `Sema` in the temporary arena, and is
    353     /// used to add a `func_instance` into the `InternPool`.
    354     params: std.MultiArrayList(Param) = .{},
    355 
    356     label: ?*Label = null,
    357     inlining: ?*Inlining,
    358     /// If runtime_index is not 0 then one of these is guaranteed to be non null.
    359     runtime_cond: ?LazySrcLoc = null,
    360     runtime_loop: ?LazySrcLoc = null,
    361     /// Non zero if a non-inline loop or a runtime conditional have been encountered.
    362     /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index.
    363     runtime_index: RuntimeIndex = .zero,
    364     inline_block: Zir.Inst.OptionalIndex = .none,
    365 
    366     comptime_reason: ?BlockComptimeReason = null,
    367     is_typeof: bool = false,
    368 
    369     /// Keep track of the active error return trace index around blocks so that we can correctly
    370     /// pop the error trace upon block exit.
    371     error_return_trace_index: Air.Inst.Ref = .none,
    372 
    373     /// when null, it is determined by build mode, changed by @setRuntimeSafety
    374     want_safety: ?bool = null,
    375 
    376     /// What mode to generate float operations in, set by @setFloatMode
    377     float_mode: std.builtin.FloatMode = .strict,
    378 
    379     c_import_buf: ?*std.array_list.Managed(u8) = null,
    380 
    381     /// If not `null`, this boolean is set when a `dbg_var_ptr`, `dbg_var_val`, or `dbg_arg_inline`.
    382     /// instruction is emitted. It signals that the innermost lexically
    383     /// enclosing `block`/`block_inline` should be translated into a real AIR
    384     /// `block` in order for codegen to match lexical scoping for debug vars.
    385     need_debug_scope: ?*bool = null,
    386 
    387     /// Relative source locations encountered while traversing this block should be
    388     /// treated as relative to the AST node of this ZIR instruction.
    389     src_base_inst: InternPool.TrackedInst.Index,
    390 
    391     /// The name of the current "context" for naming namespace types.
    392     /// The interpretation of this depends on the name strategy in ZIR, but the name
    393     /// is always incorporated into the type name somehow.
    394     /// See `Sema.createTypeName`.
    395     type_name_ctx: InternPool.NullTerminatedString,
    396 
    397     /// Create a `LazySrcLoc` based on an `Offset` from the code being analyzed in this block.
    398     /// Specifically, the given `Offset` is treated as relative to `block.src_base_inst`.
    399     pub fn src(block: Block, offset: LazySrcLoc.Offset) LazySrcLoc {
    400         return .{
    401             .base_node_inst = block.src_base_inst,
    402             .offset = offset,
    403         };
    404     }
    405 
    406     fn isComptime(block: Block) bool {
    407         return block.comptime_reason != null;
    408     }
    409 
    410     fn builtinCallArgSrc(block: *Block, builtin_call_node: std.zig.Ast.Node.Offset, arg_index: u32) LazySrcLoc {
    411         return block.src(.{ .node_offset_builtin_call_arg = .{
    412             .builtin_call_node = builtin_call_node,
    413             .arg_index = arg_index,
    414         } });
    415     }
    416 
    417     pub fn nodeOffset(block: Block, node_offset: std.zig.Ast.Node.Offset) LazySrcLoc {
    418         return block.src(LazySrcLoc.Offset.nodeOffset(node_offset));
    419     }
    420 
    421     fn tokenOffset(block: Block, tok_offset: std.zig.Ast.TokenOffset) LazySrcLoc {
    422         return block.src(.{ .token_offset = tok_offset });
    423     }
    424 
    425     const Param = struct {
    426         /// `none` means `anytype`.
    427         ty: InternPool.Index,
    428         is_comptime: bool,
    429         name: Zir.NullTerminatedString,
    430     };
    431 
    432     /// This `Block` maps a block ZIR instruction to the corresponding
    433     /// AIR instruction for break instruction analysis.
    434     pub const Label = struct {
    435         zir_block: Zir.Inst.Index,
    436         merges: Merges,
    437     };
    438 
    439     /// This `Block` indicates that an inline function call is happening
    440     /// and return instructions should be analyzed as a break instruction
    441     /// to this AIR block instruction.
    442     /// It is shared among all the blocks in an inline or comptime called
    443     /// function.
    444     pub const Inlining = struct {
    445         call_block: *Block,
    446         call_src: LazySrcLoc,
    447         func: InternPool.Index,
    448 
    449         /// Populated lazily by `refFrame`.
    450         ref_frame: Zcu.InlineReferenceFrame.Index.Optional = .none,
    451 
    452         /// If `true`, the following fields are `undefined`. This doesn't represent a true inline
    453         /// call, but rather a generic call analyzing the instantiation's generic type bodies.
    454         is_generic_instantiation: bool,
    455 
    456         has_comptime_args: bool,
    457         comptime_result: Air.Inst.Ref,
    458         merges: Merges,
    459 
    460         fn refFrame(inlining: *Inlining, zcu: *Zcu) Allocator.Error!Zcu.InlineReferenceFrame.Index {
    461             if (inlining.ref_frame == .none) {
    462                 inlining.ref_frame = (try zcu.addInlineReferenceFrame(.{
    463                     .callee = inlining.func,
    464                     .call_src = inlining.call_src,
    465                     .parent = if (inlining.call_block.inlining) |parent_inlining| p: {
    466                         break :p (try parent_inlining.refFrame(zcu)).toOptional();
    467                     } else .none,
    468                 })).toOptional();
    469             }
    470             return inlining.ref_frame.unwrap().?;
    471         }
    472     };
    473 
    474     pub const Merges = struct {
    475         block_inst: Air.Inst.Index,
    476         /// Separate array list from break_inst_list so that it can be passed directly
    477         /// to resolvePeerTypes.
    478         results: std.ArrayListUnmanaged(Air.Inst.Ref),
    479         /// Keeps track of the break instructions so that the operand can be replaced
    480         /// if we need to add type coercion at the end of block analysis.
    481         /// Same indexes, capacity, length as `results`.
    482         br_list: std.ArrayListUnmanaged(Air.Inst.Index),
    483         /// Keeps the source location of the rhs operand of the break instruction,
    484         /// to enable more precise compile errors.
    485         /// Same indexes, capacity, length as `results`.
    486         src_locs: std.ArrayListUnmanaged(?LazySrcLoc),
    487         /// Most blocks do not utilize this field. When it is used, its use is
    488         /// contextual. The possible uses are as follows:
    489         /// * for a `switch_block[_ref]`, this refers to dummy `br` instructions
    490         ///   which correspond to `switch_continue` ZIR. The switch logic will
    491         ///   rewrite these to appropriate AIR switch dispatches.
    492         extra_insts: std.ArrayListUnmanaged(Air.Inst.Index) = .empty,
    493         /// Same indexes, capacity, length as `extra_insts`.
    494         extra_src_locs: std.ArrayListUnmanaged(LazySrcLoc) = .empty,
    495 
    496         pub fn deinit(merges: *@This(), allocator: Allocator) void {
    497             merges.results.deinit(allocator);
    498             merges.br_list.deinit(allocator);
    499             merges.src_locs.deinit(allocator);
    500             merges.extra_insts.deinit(allocator);
    501             merges.extra_src_locs.deinit(allocator);
    502         }
    503     };
    504 
    505     pub fn makeSubBlock(parent: *Block) Block {
    506         return .{
    507             .parent = parent,
    508             .sema = parent.sema,
    509             .namespace = parent.namespace,
    510             .instructions = .{},
    511             .label = null,
    512             .inlining = parent.inlining,
    513             .comptime_reason = parent.comptime_reason,
    514             .is_typeof = parent.is_typeof,
    515             .runtime_cond = parent.runtime_cond,
    516             .runtime_loop = parent.runtime_loop,
    517             .runtime_index = parent.runtime_index,
    518             .want_safety = parent.want_safety,
    519             .float_mode = parent.float_mode,
    520             .c_import_buf = parent.c_import_buf,
    521             .error_return_trace_index = parent.error_return_trace_index,
    522             .need_debug_scope = parent.need_debug_scope,
    523             .src_base_inst = parent.src_base_inst,
    524             .type_name_ctx = parent.type_name_ctx,
    525         };
    526     }
    527 
    528     fn wantSafeTypes(block: *const Block) bool {
    529         return block.want_safety orelse switch (block.sema.pt.zcu.optimizeMode()) {
    530             .Debug => true,
    531             .ReleaseSafe => true,
    532             .ReleaseFast => false,
    533             .ReleaseSmall => false,
    534         };
    535     }
    536 
    537     fn wantSafety(block: *const Block) bool {
    538         if (block.isComptime()) return false; // runtime safety checks are pointless in comptime blocks
    539         return block.want_safety orelse switch (block.sema.pt.zcu.optimizeMode()) {
    540             .Debug => true,
    541             .ReleaseSafe => true,
    542             .ReleaseFast => false,
    543             .ReleaseSmall => false,
    544         };
    545     }
    546 
    547     pub fn getFileScope(block: *Block, zcu: *Zcu) *Zcu.File {
    548         return zcu.fileByIndex(getFileScopeIndex(block, zcu));
    549     }
    550 
    551     pub fn getFileScopeIndex(block: *Block, zcu: *Zcu) Zcu.File.Index {
    552         return zcu.namespacePtr(block.namespace).file_scope;
    553     }
    554 
    555     fn addTy(
    556         block: *Block,
    557         tag: Air.Inst.Tag,
    558         ty: Type,
    559     ) error{OutOfMemory}!Air.Inst.Ref {
    560         return block.addInst(.{
    561             .tag = tag,
    562             .data = .{ .ty = ty },
    563         });
    564     }
    565 
    566     fn addTyOp(
    567         block: *Block,
    568         tag: Air.Inst.Tag,
    569         ty: Type,
    570         operand: Air.Inst.Ref,
    571     ) error{OutOfMemory}!Air.Inst.Ref {
    572         return block.addInst(.{
    573             .tag = tag,
    574             .data = .{ .ty_op = .{
    575                 .ty = Air.internedToRef(ty.toIntern()),
    576                 .operand = operand,
    577             } },
    578         });
    579     }
    580 
    581     fn addBitCast(block: *Block, ty: Type, operand: Air.Inst.Ref) Allocator.Error!Air.Inst.Ref {
    582         return block.addInst(.{
    583             .tag = .bitcast,
    584             .data = .{ .ty_op = .{
    585                 .ty = Air.internedToRef(ty.toIntern()),
    586                 .operand = operand,
    587             } },
    588         });
    589     }
    590 
    591     fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
    592         return block.addInst(.{
    593             .tag = tag,
    594             .data = .{ .no_op = {} },
    595         });
    596     }
    597 
    598     fn addUnOp(
    599         block: *Block,
    600         tag: Air.Inst.Tag,
    601         operand: Air.Inst.Ref,
    602     ) error{OutOfMemory}!Air.Inst.Ref {
    603         return block.addInst(.{
    604             .tag = tag,
    605             .data = .{ .un_op = operand },
    606         });
    607     }
    608 
    609     fn addBr(
    610         block: *Block,
    611         target_block: Air.Inst.Index,
    612         operand: Air.Inst.Ref,
    613     ) error{OutOfMemory}!Air.Inst.Ref {
    614         return block.addInst(.{
    615             .tag = .br,
    616             .data = .{ .br = .{
    617                 .block_inst = target_block,
    618                 .operand = operand,
    619             } },
    620         });
    621     }
    622 
    623     fn addBinOp(
    624         block: *Block,
    625         tag: Air.Inst.Tag,
    626         lhs: Air.Inst.Ref,
    627         rhs: Air.Inst.Ref,
    628     ) error{OutOfMemory}!Air.Inst.Ref {
    629         return block.addInst(.{
    630             .tag = tag,
    631             .data = .{ .bin_op = .{
    632                 .lhs = lhs,
    633                 .rhs = rhs,
    634             } },
    635         });
    636     }
    637 
    638     fn addStructFieldPtr(
    639         block: *Block,
    640         struct_ptr: Air.Inst.Ref,
    641         field_index: u32,
    642         ptr_field_ty: Type,
    643     ) !Air.Inst.Ref {
    644         const ty = Air.internedToRef(ptr_field_ty.toIntern());
    645         const tag: Air.Inst.Tag = switch (field_index) {
    646             0 => .struct_field_ptr_index_0,
    647             1 => .struct_field_ptr_index_1,
    648             2 => .struct_field_ptr_index_2,
    649             3 => .struct_field_ptr_index_3,
    650             else => {
    651                 return block.addInst(.{
    652                     .tag = .struct_field_ptr,
    653                     .data = .{ .ty_pl = .{
    654                         .ty = ty,
    655                         .payload = try block.sema.addExtra(Air.StructField{
    656                             .struct_operand = struct_ptr,
    657                             .field_index = field_index,
    658                         }),
    659                     } },
    660                 });
    661             },
    662         };
    663         return block.addInst(.{
    664             .tag = tag,
    665             .data = .{ .ty_op = .{
    666                 .ty = ty,
    667                 .operand = struct_ptr,
    668             } },
    669         });
    670     }
    671 
    672     fn addStructFieldVal(
    673         block: *Block,
    674         struct_val: Air.Inst.Ref,
    675         field_index: u32,
    676         field_ty: Type,
    677     ) !Air.Inst.Ref {
    678         return block.addInst(.{
    679             .tag = .struct_field_val,
    680             .data = .{ .ty_pl = .{
    681                 .ty = Air.internedToRef(field_ty.toIntern()),
    682                 .payload = try block.sema.addExtra(Air.StructField{
    683                     .struct_operand = struct_val,
    684                     .field_index = field_index,
    685                 }),
    686             } },
    687         });
    688     }
    689 
    690     fn addSliceElemPtr(
    691         block: *Block,
    692         slice: Air.Inst.Ref,
    693         elem_index: Air.Inst.Ref,
    694         elem_ptr_ty: Type,
    695     ) !Air.Inst.Ref {
    696         return block.addInst(.{
    697             .tag = .slice_elem_ptr,
    698             .data = .{ .ty_pl = .{
    699                 .ty = Air.internedToRef(elem_ptr_ty.toIntern()),
    700                 .payload = try block.sema.addExtra(Air.Bin{
    701                     .lhs = slice,
    702                     .rhs = elem_index,
    703                 }),
    704             } },
    705         });
    706     }
    707 
    708     fn addPtrElemPtr(
    709         block: *Block,
    710         array_ptr: Air.Inst.Ref,
    711         elem_index: Air.Inst.Ref,
    712         elem_ptr_ty: Type,
    713     ) !Air.Inst.Ref {
    714         const ty_ref = Air.internedToRef(elem_ptr_ty.toIntern());
    715         return block.addPtrElemPtrTypeRef(array_ptr, elem_index, ty_ref);
    716     }
    717 
    718     fn addPtrElemPtrTypeRef(
    719         block: *Block,
    720         array_ptr: Air.Inst.Ref,
    721         elem_index: Air.Inst.Ref,
    722         elem_ptr_ty: Air.Inst.Ref,
    723     ) !Air.Inst.Ref {
    724         return block.addInst(.{
    725             .tag = .ptr_elem_ptr,
    726             .data = .{ .ty_pl = .{
    727                 .ty = elem_ptr_ty,
    728                 .payload = try block.sema.addExtra(Air.Bin{
    729                     .lhs = array_ptr,
    730                     .rhs = elem_index,
    731                 }),
    732             } },
    733         });
    734     }
    735 
    736     fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref {
    737         const sema = block.sema;
    738         const pt = sema.pt;
    739         const zcu = pt.zcu;
    740         return block.addInst(.{
    741             .tag = if (block.float_mode == .optimized) .cmp_vector_optimized else .cmp_vector,
    742             .data = .{ .ty_pl = .{
    743                 .ty = Air.internedToRef((try pt.vectorType(.{
    744                     .len = sema.typeOf(lhs).vectorLen(zcu),
    745                     .child = .bool_type,
    746                 })).toIntern()),
    747                 .payload = try sema.addExtra(Air.VectorCmp{
    748                     .lhs = lhs,
    749                     .rhs = rhs,
    750                     .op = Air.VectorCmp.encodeOp(cmp_op),
    751                 }),
    752             } },
    753         });
    754     }
    755 
    756     fn addReduce(block: *Block, operand: Air.Inst.Ref, operation: std.builtin.ReduceOp) !Air.Inst.Ref {
    757         const sema = block.sema;
    758         const zcu = sema.pt.zcu;
    759         const allow_optimized = switch (sema.typeOf(operand).childType(zcu).zigTypeTag(zcu)) {
    760             .float => true,
    761             .bool, .int => false,
    762             else => unreachable,
    763         };
    764         return block.addInst(.{
    765             .tag = if (allow_optimized and block.float_mode == .optimized) .reduce_optimized else .reduce,
    766             .data = .{ .reduce = .{
    767                 .operand = operand,
    768                 .operation = operation,
    769             } },
    770         });
    771     }
    772 
    773     fn addAggregateInit(
    774         block: *Block,
    775         aggregate_ty: Type,
    776         elements: []const Air.Inst.Ref,
    777     ) !Air.Inst.Ref {
    778         const sema = block.sema;
    779         const ty_ref = Air.internedToRef(aggregate_ty.toIntern());
    780         try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len);
    781         const extra_index: u32 = @intCast(sema.air_extra.items.len);
    782         sema.appendRefsAssumeCapacity(elements);
    783 
    784         return block.addInst(.{
    785             .tag = .aggregate_init,
    786             .data = .{ .ty_pl = .{
    787                 .ty = ty_ref,
    788                 .payload = extra_index,
    789             } },
    790         });
    791     }
    792 
    793     fn addUnionInit(
    794         block: *Block,
    795         union_ty: Type,
    796         field_index: u32,
    797         init: Air.Inst.Ref,
    798     ) !Air.Inst.Ref {
    799         return block.addInst(.{
    800             .tag = .union_init,
    801             .data = .{ .ty_pl = .{
    802                 .ty = Air.internedToRef(union_ty.toIntern()),
    803                 .payload = try block.sema.addExtra(Air.UnionInit{
    804                     .field_index = field_index,
    805                     .init = init,
    806                 }),
    807             } },
    808         });
    809     }
    810 
    811     pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
    812         return (try block.addInstAsIndex(inst)).toRef();
    813     }
    814 
    815     pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
    816         const sema = block.sema;
    817         const gpa = sema.gpa;
    818 
    819         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
    820         try block.instructions.ensureUnusedCapacity(gpa, 1);
    821 
    822         const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
    823         sema.air_instructions.appendAssumeCapacity(inst);
    824         block.instructions.appendAssumeCapacity(result_index);
    825         return result_index;
    826     }
    827 
    828     /// Insert an instruction into the block at `index`. Moves all following
    829     /// instructions forward in the block to make room. Operation is O(N).
    830     pub fn insertInst(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
    831         return (try block.insertInstAsIndex(index, inst)).toRef();
    832     }
    833 
    834     pub fn insertInstAsIndex(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
    835         const sema = block.sema;
    836         const gpa = sema.gpa;
    837 
    838         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
    839 
    840         const result_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
    841         sema.air_instructions.appendAssumeCapacity(inst);
    842 
    843         try block.instructions.insert(gpa, @intFromEnum(index), result_index);
    844         return result_index;
    845     }
    846 
    847     pub fn ownerModule(block: Block) *Package.Module {
    848         const zcu = block.sema.pt.zcu;
    849         return zcu.namespacePtr(block.namespace).fileScope(zcu).mod.?;
    850     }
    851 
    852     fn trackZir(block: *Block, inst: Zir.Inst.Index) Allocator.Error!InternPool.TrackedInst.Index {
    853         const pt = block.sema.pt;
    854         block.sema.code.assertTrackable(inst);
    855         return pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{
    856             .file = block.getFileScopeIndex(pt.zcu),
    857             .inst = inst,
    858         });
    859     }
    860 
    861     /// Returns the `*Block` that should be passed to `Sema.failWithOwnedErrorMsg`, because all inline
    862     /// calls below it have already been reported with "called at comptime from here" notes.
    863     fn explainWhyBlockIsComptime(start_block: *Block, err_msg: *Zcu.ErrorMsg) !*Block {
    864         const sema = start_block.sema;
    865         var block = start_block;
    866         while (true) {
    867             switch (block.comptime_reason.?) {
    868                 .inlining_parent => {
    869                     const inlining = block.inlining.?;
    870                     try sema.errNote(inlining.call_src, err_msg, "called at comptime from here", .{});
    871                     block = inlining.call_block;
    872                 },
    873                 .reason => |r| {
    874                     try r.r.explain(sema, r.src, err_msg);
    875                     return block;
    876                 },
    877             }
    878         }
    879     }
    880 };
    881 
    882 /// Represents the reason we are resolving a value or evaluating code at comptime.
    883 /// Most reasons are represented by a `std.zig.SimpleComptimeReason`, which provides a plain message.
    884 const ComptimeReason = union(enum) {
    885     /// Evaluating at comptime for a reason in the `std.zig.SimpleComptimeReason` enum.
    886     simple: std.zig.SimpleComptimeReason,
    887 
    888     /// Evaluating at comptime because of a comptime-only type. This field is separate so that
    889     /// the type in question can be included in the error message. AstGen could never emit this
    890     /// reason, because it knows nothing of types.
    891     /// The format string looks like "foo '{f}' bar", where "{f}" is the comptime-only type.
    892     /// We will then explain why this type is comptime-only.
    893     comptime_only: struct {
    894         ty: Type,
    895         msg: enum {
    896             union_init,
    897             struct_init,
    898             tuple_init,
    899         },
    900     },
    901 
    902     /// Like `comptime_only`, but for a parameter type.
    903     /// Includes a "parameter type declared here" note.
    904     comptime_only_param_ty: struct {
    905         ty: Type,
    906         param_ty_src: LazySrcLoc,
    907     },
    908 
    909     /// Like `comptime_only`, but for a return type.
    910     /// Includes a "return type declared here" note.
    911     comptime_only_ret_ty: struct {
    912         ty: Type,
    913         is_generic_inst: bool,
    914         ret_ty_src: LazySrcLoc,
    915     },
    916 
    917     /// Evaluating at comptime because we're evaluating an argument to a parameter marked `comptime`.
    918     comptime_param: struct {
    919         comptime_src: LazySrcLoc,
    920     },
    921 
    922     fn explain(reason: ComptimeReason, sema: *Sema, src: LazySrcLoc, err_msg: *Zcu.ErrorMsg) !void {
    923         switch (reason) {
    924             .simple => |simple| {
    925                 try sema.errNote(src, err_msg, "{s}", .{simple.message()});
    926             },
    927             .comptime_only => |co| {
    928                 const pre, const post = switch (co.msg) {
    929                     .union_init => .{ "initializer of comptime-only union", "must be comptime-known" },
    930                     .struct_init => .{ "initializer of comptime-only struct", "must be comptime-known" },
    931                     .tuple_init => .{ "initializer of comptime-only tuple", "must be comptime-known" },
    932                 };
    933                 try sema.errNote(src, err_msg, "{s} '{f}' {s}", .{ pre, co.ty.fmt(sema.pt), post });
    934                 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty);
    935             },
    936             .comptime_only_param_ty => |co| {
    937                 try sema.errNote(src, err_msg, "argument to parameter with comptime-only type '{f}' must be comptime-known", .{co.ty.fmt(sema.pt)});
    938                 try sema.errNote(co.param_ty_src, err_msg, "parameter type declared here", .{});
    939                 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty);
    940             },
    941             .comptime_only_ret_ty => |co| {
    942                 const function_with: []const u8 = if (co.is_generic_inst) "generic function instantiated with" else "function with";
    943                 try sema.errNote(src, err_msg, "call to {s} comptime-only return type '{f}' is evaluated at comptime", .{ function_with, co.ty.fmt(sema.pt) });
    944                 try sema.errNote(co.ret_ty_src, err_msg, "return type declared here", .{});
    945                 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty);
    946             },
    947             .comptime_param => |cp| {
    948                 try sema.errNote(src, err_msg, "argument to comptime parameter must be comptime-known", .{});
    949                 try sema.errNote(cp.comptime_src, err_msg, "parameter declared comptime here", .{});
    950             },
    951         }
    952     }
    953 };
    954 
    955 /// Represents the reason a `Block` is being evaluated at comptime.
    956 const BlockComptimeReason = union(enum) {
    957     /// This block inherits being comptime-only from the `inlining` call site.
    958     inlining_parent,
    959 
    960     /// Comptime evaluation began somewhere in the current function for a given `ComptimeReason`.
    961     reason: struct {
    962         /// The source location which this reason originates from. `r` is reported here.
    963         src: LazySrcLoc,
    964         r: ComptimeReason,
    965     },
    966 };
    967 
    968 const LabeledBlock = struct {
    969     block: Block,
    970     label: Block.Label,
    971 
    972     fn destroy(lb: *LabeledBlock, gpa: Allocator) void {
    973         lb.block.instructions.deinit(gpa);
    974         lb.label.merges.deinit(gpa);
    975         gpa.destroy(lb);
    976     }
    977 };
    978 
    979 /// The value stored in the inferred allocation. This will go into
    980 /// peer type resolution. This is stored in a separate list so that
    981 /// the items are contiguous in memory and thus can be passed to
    982 /// `Zcu.resolvePeerTypes`.
    983 const InferredAlloc = struct {
    984     /// The placeholder `store` instructions used before the result pointer type
    985     /// is known. These should be rewritten to perform any required coercions
    986     /// when the type is resolved.
    987     /// Allocated from `sema.arena`.
    988     prongs: std.ArrayListUnmanaged(Air.Inst.Index) = .empty,
    989 };
    990 
    991 pub fn deinit(sema: *Sema) void {
    992     const gpa = sema.gpa;
    993     sema.air_instructions.deinit(gpa);
    994     sema.air_extra.deinit(gpa);
    995     sema.inst_map.deinit(gpa);
    996     {
    997         var it = sema.post_hoc_blocks.iterator();
    998         while (it.next()) |entry| {
    999             const labeled_block = entry.value_ptr.*;
   1000             labeled_block.destroy(gpa);
   1001         }
   1002         sema.post_hoc_blocks.deinit(gpa);
   1003     }
   1004     sema.unresolved_inferred_allocs.deinit(gpa);
   1005     sema.base_allocs.deinit(gpa);
   1006     sema.maybe_comptime_allocs.deinit(gpa);
   1007     sema.comptime_allocs.deinit(gpa);
   1008     sema.exports.deinit(gpa);
   1009     sema.references.deinit(gpa);
   1010     sema.type_references.deinit(gpa);
   1011     sema.dependencies.deinit(gpa);
   1012     sema.* = undefined;
   1013 }
   1014 
   1015 /// Performs semantic analysis of a ZIR body which is behind a runtime condition. If comptime
   1016 /// control flow happens here, Sema will convert it to runtime control flow by introducing post-hoc
   1017 /// blocks where necessary.
   1018 /// Returns the branch hint for this branch.
   1019 fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !std.builtin.BranchHint {
   1020     const parent_hint = sema.branch_hint;
   1021     defer sema.branch_hint = parent_hint;
   1022     sema.branch_hint = null;
   1023 
   1024     sema.analyzeBodyInner(block, body) catch |err| switch (err) {
   1025         error.ComptimeBreak => {
   1026             const zir_datas = sema.code.instructions.items(.data);
   1027             const break_data = zir_datas[@intFromEnum(sema.comptime_break_inst)].@"break";
   1028             const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
   1029             try sema.addRuntimeBreak(block, extra.block_inst, break_data.operand);
   1030         },
   1031         else => |e| return e,
   1032     };
   1033 
   1034     return sema.branch_hint orelse .none;
   1035 }
   1036 
   1037 /// Semantically analyze a ZIR function body. It is guranteed by AstGen that such a body cannot
   1038 /// trigger comptime control flow to move above the function body.
   1039 pub fn analyzeFnBody(
   1040     sema: *Sema,
   1041     block: *Block,
   1042     body: []const Zir.Inst.Index,
   1043 ) !void {
   1044     sema.analyzeBodyInner(block, body) catch |err| switch (err) {
   1045         error.ComptimeBreak => unreachable, // unexpected comptime control flow
   1046         else => |e| return e,
   1047     };
   1048 }
   1049 
   1050 /// Given a ZIR body which can be exited via a `break_inline` instruction, or a non-inline body which
   1051 /// we are evaluating at comptime, semantically analyze the body and return the result from it.
   1052 /// Returns `null` if control flow did not break from this block, but instead terminated with some
   1053 /// other runtime noreturn instruction. Compile-time breaks to blocks further up the stack still
   1054 /// return `error.ComptimeBreak`. If `block.isComptime()`, this function will never return `null`.
   1055 fn analyzeInlineBody(
   1056     sema: *Sema,
   1057     block: *Block,
   1058     body: []const Zir.Inst.Index,
   1059     /// The index which a break instruction can target to break from this body.
   1060     break_target: Zir.Inst.Index,
   1061 ) CompileError!?Air.Inst.Ref {
   1062     if (sema.analyzeBodyInner(block, body)) |_| {
   1063         return null;
   1064     } else |err| switch (err) {
   1065         error.ComptimeBreak => {},
   1066         else => |e| return e,
   1067     }
   1068     const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst));
   1069     switch (break_inst.tag) {
   1070         .switch_continue => {
   1071             // This is handled by separate logic.
   1072             return error.ComptimeBreak;
   1073         },
   1074         .break_inline, .@"break" => {},
   1075         else => unreachable,
   1076     }
   1077     const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data;
   1078     if (extra.block_inst != break_target) {
   1079         // This control flow goes further up the stack.
   1080         return error.ComptimeBreak;
   1081     }
   1082     return try sema.resolveInst(break_inst.data.@"break".operand);
   1083 }
   1084 
   1085 /// Like `analyzeInlineBody`, but if the body does not break with a value, returns
   1086 /// `.unreachable_value` instead of `null`. Notably, use this to evaluate an arbitrary
   1087 /// body at comptime to a single result value.
   1088 pub fn resolveInlineBody(
   1089     sema: *Sema,
   1090     block: *Block,
   1091     body: []const Zir.Inst.Index,
   1092     /// The index which a break instruction can target to break from this body.
   1093     break_target: Zir.Inst.Index,
   1094 ) CompileError!Air.Inst.Ref {
   1095     return (try sema.analyzeInlineBody(block, body, break_target)) orelse .unreachable_value;
   1096 }
   1097 
   1098 /// This function is the main loop of `Sema`. It analyzes a single body of ZIR instructions.
   1099 ///
   1100 /// If this function returns normally, the merges of `block` were populated with all possible
   1101 /// (runtime) results of this block. Peer type resolution should be performed on the result,
   1102 /// and relevant runtime instructions written to perform necessary coercions and breaks. See
   1103 /// `resolveAnalyzedBlock`. This form of return is impossible if `block.isComptime()`.
   1104 ///
   1105 /// Alternatively, this function may return `error.ComptimeBreak`. This indicates that comptime
   1106 /// control flow is happening, and we are breaking at comptime from a block indicated by the
   1107 /// break instruction in `sema.comptime_break_inst`. This occurs for any `break_inline`, or for a
   1108 /// standard `break` at comptime. This error is pushed up the stack until the target block is
   1109 /// reached, at which point the break operand will be fetched.
   1110 ///
   1111 /// It is rare to call this function directly. Usually, you want one of the following wrappers:
   1112 /// * If the body is exited via a `break_inline`, or is being evaluated at comptime,
   1113 ///   use `Sema.analyzeInlineBody` or `Sema.resolveInlineBody`.
   1114 /// * If the body is behind a fresh runtime condition, use `Sema.analyzeBodyRuntimeBreak`.
   1115 /// * If the body is an entire function body, use `Sema.analyzeFnBody`.
   1116 /// * If the body is to be generated into an AIR `block`, use `Sema.resolveBlockBody`.
   1117 /// * Otherwise, direct usage of `Sema.analyzeBodyInner` may be necessary.
   1118 fn analyzeBodyInner(
   1119     sema: *Sema,
   1120     block: *Block,
   1121     body: []const Zir.Inst.Index,
   1122 ) CompileError!void {
   1123     // No tracy calls here, to avoid interfering with the tail call mechanism.
   1124 
   1125     try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body);
   1126 
   1127     const pt = sema.pt;
   1128     const zcu = pt.zcu;
   1129     const map = &sema.inst_map;
   1130     const tags = sema.code.instructions.items(.tag);
   1131     const datas = sema.code.instructions.items(.data);
   1132 
   1133     var crash_info = crash_report.prepAnalyzeBody(sema, block, body);
   1134     crash_info.push();
   1135     defer crash_info.pop();
   1136 
   1137     // We use a while (true) loop here to avoid a redundant way of breaking out of
   1138     // the loop. The only way to break out of the loop is with a `noreturn`
   1139     // instruction.
   1140     var i: u32 = 0;
   1141     while (true) {
   1142         crash_info.setBodyIndex(i);
   1143         const inst = body[i];
   1144 
   1145         // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away.
   1146         if (build_options.enable_logging) {
   1147             std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path: {
   1148                 const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool);
   1149                 const file = zcu.fileByIndex(file_index);
   1150                 break :path file.path.fmt(zcu.comp);
   1151             }, inst });
   1152         }
   1153 
   1154         const air_inst: Air.Inst.Ref = inst: switch (tags[@intFromEnum(inst)]) {
   1155             // zig fmt: off
   1156             .alloc                        => try sema.zirAlloc(block, inst),
   1157             .alloc_inferred               => try sema.zirAllocInferred(block, true),
   1158             .alloc_inferred_mut           => try sema.zirAllocInferred(block, false),
   1159             .alloc_inferred_comptime      => try sema.zirAllocInferredComptime(true),
   1160             .alloc_inferred_comptime_mut  => try sema.zirAllocInferredComptime(false),
   1161             .resolve_inferred_alloc       => try sema.zirResolveInferredAlloc(block, inst),
   1162             .alloc_mut                    => try sema.zirAllocMut(block, inst),
   1163             .alloc_comptime_mut           => try sema.zirAllocComptime(block, inst),
   1164             .make_ptr_const               => try sema.zirMakePtrConst(block, inst),
   1165             .anyframe_type                => try sema.zirAnyframeType(block, inst),
   1166             .array_cat                    => try sema.zirArrayCat(block, inst),
   1167             .array_mul                    => try sema.zirArrayMul(block, inst),
   1168             .array_type                   => try sema.zirArrayType(block, inst),
   1169             .array_type_sentinel          => try sema.zirArrayTypeSentinel(block, inst),
   1170             .vector_type                  => try sema.zirVectorType(block, inst),
   1171             .as_node                      => try sema.zirAsNode(block, inst),
   1172             .as_shift_operand             => try sema.zirAsShiftOperand(block, inst),
   1173             .bit_and                      => try sema.zirBitwise(block, inst, .bit_and),
   1174             .bit_not                      => try sema.zirBitNot(block, inst),
   1175             .bit_or                       => try sema.zirBitwise(block, inst, .bit_or),
   1176             .bitcast                      => try sema.zirBitcast(block, inst),
   1177             .suspend_block                => try sema.zirSuspendBlock(block, inst),
   1178             .bool_not                     => try sema.zirBoolNot(block, inst),
   1179             .bool_br_and                  => try sema.zirBoolBr(block, inst, false),
   1180             .bool_br_or                   => try sema.zirBoolBr(block, inst, true),
   1181             .c_import                     => try sema.zirCImport(block, inst),
   1182             .call                         => try sema.zirCall(block, inst, .direct),
   1183             .field_call                   => try sema.zirCall(block, inst, .field),
   1184             .cmp_lt                       => try sema.zirCmp(block, inst, .lt),
   1185             .cmp_lte                      => try sema.zirCmp(block, inst, .lte),
   1186             .cmp_eq                       => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .optimized)),
   1187             .cmp_gte                      => try sema.zirCmp(block, inst, .gte),
   1188             .cmp_gt                       => try sema.zirCmp(block, inst, .gt),
   1189             .cmp_neq                      => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .optimized)),
   1190             .decl_ref                     => try sema.zirDeclRef(block, inst),
   1191             .decl_val                     => try sema.zirDeclVal(block, inst),
   1192             .load                         => try sema.zirLoad(block, inst),
   1193             .elem_ptr                     => try sema.zirElemPtr(block, inst),
   1194             .elem_ptr_node                => try sema.zirElemPtrNode(block, inst),
   1195             .elem_val                     => try sema.zirElemVal(block, inst),
   1196             .elem_ptr_load                => try sema.zirElemPtrLoad(block, inst),
   1197             .elem_val_imm                 => try sema.zirElemValImm(block, inst),
   1198             .elem_type                    => try sema.zirElemType(block, inst),
   1199             .indexable_ptr_elem_type      => try sema.zirIndexablePtrElemType(block, inst),
   1200             .splat_op_result_ty           => try sema.zirSplatOpResultType(block, inst),
   1201             .enum_literal                 => try sema.zirEnumLiteral(block, inst),
   1202             .decl_literal                 => try sema.zirDeclLiteral(block, inst, true),
   1203             .decl_literal_no_coerce       => try sema.zirDeclLiteral(block, inst, false),
   1204             .int_from_enum                => try sema.zirIntFromEnum(block, inst),
   1205             .enum_from_int                => try sema.zirEnumFromInt(block, inst),
   1206             .err_union_code               => try sema.zirErrUnionCode(block, inst),
   1207             .err_union_code_ptr           => try sema.zirErrUnionCodePtr(block, inst),
   1208             .err_union_payload_unsafe     => try sema.zirErrUnionPayload(block, inst),
   1209             .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst),
   1210             .error_union_type             => try sema.zirErrorUnionType(block, inst),
   1211             .error_value                  => try sema.zirErrorValue(block, inst),
   1212             .field_ptr                    => try sema.zirFieldPtr(block, inst),
   1213             .field_ptr_named              => try sema.zirFieldPtrNamed(block, inst),
   1214             .field_ptr_load               => try sema.zirFieldPtrLoad(block, inst),
   1215             .field_ptr_named_load         => try sema.zirFieldPtrNamedLoad(block, inst),
   1216             .func                         => try sema.zirFunc(block, inst, false),
   1217             .func_inferred                => try sema.zirFunc(block, inst, true),
   1218             .func_fancy                   => try sema.zirFuncFancy(block, inst),
   1219             .import                       => try sema.zirImport(block, inst),
   1220             .indexable_ptr_len            => try sema.zirIndexablePtrLen(block, inst),
   1221             .int                          => try sema.zirInt(block, inst),
   1222             .int_big                      => try sema.zirIntBig(block, inst),
   1223             .float                        => try sema.zirFloat(block, inst),
   1224             .float128                     => try sema.zirFloat128(block, inst),
   1225             .int_type                     => try sema.zirIntType(inst),
   1226             .is_non_err                   => try sema.zirIsNonErr(block, inst),
   1227             .is_non_err_ptr               => try sema.zirIsNonErrPtr(block, inst),
   1228             .ret_is_non_err               => try sema.zirRetIsNonErr(block, inst),
   1229             .is_non_null                  => try sema.zirIsNonNull(block, inst),
   1230             .is_non_null_ptr              => try sema.zirIsNonNullPtr(block, inst),
   1231             .merge_error_sets             => try sema.zirMergeErrorSets(block, inst),
   1232             .negate                       => try sema.zirNegate(block, inst),
   1233             .negate_wrap                  => try sema.zirNegateWrap(block, inst),
   1234             .optional_payload_safe        => try sema.zirOptionalPayload(block, inst, true),
   1235             .optional_payload_safe_ptr    => try sema.zirOptionalPayloadPtr(block, inst, true),
   1236             .optional_payload_unsafe      => try sema.zirOptionalPayload(block, inst, false),
   1237             .optional_payload_unsafe_ptr  => try sema.zirOptionalPayloadPtr(block, inst, false),
   1238             .optional_type                => try sema.zirOptionalType(block, inst),
   1239             .ptr_type                     => try sema.zirPtrType(block, inst),
   1240             .ref                          => try sema.zirRef(block, inst),
   1241             .ret_err_value_code           => try sema.zirRetErrValueCode(inst),
   1242             .shr                          => try sema.zirShr(block, inst, .shr),
   1243             .shr_exact                    => try sema.zirShr(block, inst, .shr_exact),
   1244             .slice_end                    => try sema.zirSliceEnd(block, inst),
   1245             .slice_sentinel               => try sema.zirSliceSentinel(block, inst),
   1246             .slice_start                  => try sema.zirSliceStart(block, inst),
   1247             .slice_length                 => try sema.zirSliceLength(block, inst),
   1248             .slice_sentinel_ty            => try sema.zirSliceSentinelTy(block, inst),
   1249             .str                          => try sema.zirStr(inst),
   1250             .switch_block                 => try sema.zirSwitchBlock(block, inst, false),
   1251             .switch_block_ref             => try sema.zirSwitchBlock(block, inst, true),
   1252             .switch_block_err_union       => try sema.zirSwitchBlockErrUnion(block, inst),
   1253             .type_info                    => try sema.zirTypeInfo(block, inst),
   1254             .size_of                      => try sema.zirSizeOf(block, inst),
   1255             .bit_size_of                  => try sema.zirBitSizeOf(block, inst),
   1256             .typeof                       => try sema.zirTypeof(block, inst),
   1257             .typeof_builtin               => try sema.zirTypeofBuiltin(block, inst),
   1258             .typeof_log2_int_type         => try sema.zirTypeofLog2IntType(block, inst),
   1259             .xor                          => try sema.zirBitwise(block, inst, .xor),
   1260             .struct_init_empty            => try sema.zirStructInitEmpty(block, inst),
   1261             .struct_init_empty_result     => try sema.zirStructInitEmptyResult(block, inst, false),
   1262             .struct_init_empty_ref_result => try sema.zirStructInitEmptyResult(block, inst, true),
   1263             .struct_init_anon             => try sema.zirStructInitAnon(block, inst),
   1264             .struct_init                  => try sema.zirStructInit(block, inst, false),
   1265             .struct_init_ref              => try sema.zirStructInit(block, inst, true),
   1266             .struct_init_field_type       => try sema.zirStructInitFieldType(block, inst),
   1267             .struct_init_field_ptr        => try sema.zirStructInitFieldPtr(block, inst),
   1268             .array_init_anon              => try sema.zirArrayInitAnon(block, inst),
   1269             .array_init                   => try sema.zirArrayInit(block, inst, false),
   1270             .array_init_ref               => try sema.zirArrayInit(block, inst, true),
   1271             .array_init_elem_type         => try sema.zirArrayInitElemType(block, inst),
   1272             .array_init_elem_ptr          => try sema.zirArrayInitElemPtr(block, inst),
   1273             .union_init                   => try sema.zirUnionInit(block, inst),
   1274             .field_type_ref               => try sema.zirFieldTypeRef(block, inst),
   1275             .int_from_ptr                 => try sema.zirIntFromPtr(block, inst),
   1276             .align_of                     => try sema.zirAlignOf(block, inst),
   1277             .int_from_bool                => try sema.zirIntFromBool(block, inst),
   1278             .embed_file                   => try sema.zirEmbedFile(block, inst),
   1279             .error_name                   => try sema.zirErrorName(block, inst),
   1280             .tag_name                     => try sema.zirTagName(block, inst),
   1281             .type_name                    => try sema.zirTypeName(block, inst),
   1282             .frame_type                   => try sema.zirFrameType(block, inst),
   1283             .int_from_float               => try sema.zirIntFromFloat(block, inst),
   1284             .float_from_int               => try sema.zirFloatFromInt(block, inst),
   1285             .ptr_from_int                 => try sema.zirPtrFromInt(block, inst),
   1286             .float_cast                   => try sema.zirFloatCast(block, inst),
   1287             .int_cast                     => try sema.zirIntCast(block, inst),
   1288             .ptr_cast                     => try sema.zirPtrCast(block, inst),
   1289             .truncate                     => try sema.zirTruncate(block, inst),
   1290             .has_decl                     => try sema.zirHasDecl(block, inst),
   1291             .has_field                    => try sema.zirHasField(block, inst),
   1292             .byte_swap                    => try sema.zirByteSwap(block, inst),
   1293             .bit_reverse                  => try sema.zirBitReverse(block, inst),
   1294             .bit_offset_of                => try sema.zirBitOffsetOf(block, inst),
   1295             .offset_of                    => try sema.zirOffsetOf(block, inst),
   1296             .splat                        => try sema.zirSplat(block, inst),
   1297             .reduce                       => try sema.zirReduce(block, inst),
   1298             .shuffle                      => try sema.zirShuffle(block, inst),
   1299             .atomic_load                  => try sema.zirAtomicLoad(block, inst),
   1300             .atomic_rmw                   => try sema.zirAtomicRmw(block, inst),
   1301             .mul_add                      => try sema.zirMulAdd(block, inst),
   1302             .builtin_call                 => try sema.zirBuiltinCall(block, inst),
   1303             .@"resume"                    => try sema.zirResume(block, inst),
   1304             .for_len                      => try sema.zirForLen(block, inst),
   1305             .validate_array_init_ref_ty   => try sema.zirValidateArrayInitRefTy(block, inst),
   1306             .opt_eu_base_ptr_init         => try sema.zirOptEuBasePtrInit(block, inst),
   1307             .coerce_ptr_elem_ty           => try sema.zirCoercePtrElemTy(block, inst),
   1308 
   1309             .clz       => try sema.zirBitCount(block, inst, .clz,      Value.clz),
   1310             .ctz       => try sema.zirBitCount(block, inst, .ctz,      Value.ctz),
   1311             .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount),
   1312             .abs       => try sema.zirAbs(block, inst),
   1313 
   1314             .sqrt  => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt),
   1315             .sin   => try sema.zirUnaryMath(block, inst, .sin, Value.sin),
   1316             .cos   => try sema.zirUnaryMath(block, inst, .cos, Value.cos),
   1317             .tan   => try sema.zirUnaryMath(block, inst, .tan, Value.tan),
   1318             .exp   => try sema.zirUnaryMath(block, inst, .exp, Value.exp),
   1319             .exp2  => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2),
   1320             .log   => try sema.zirUnaryMath(block, inst, .log, Value.log),
   1321             .log2  => try sema.zirUnaryMath(block, inst, .log2, Value.log2),
   1322             .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10),
   1323             .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor),
   1324             .ceil  => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil),
   1325             .round => try sema.zirUnaryMath(block, inst, .round, Value.round),
   1326             .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc),
   1327 
   1328             .error_set_decl => try sema.zirErrorSetDecl(inst),
   1329 
   1330             .add        => try sema.zirArithmetic(block, inst, .add,        true),
   1331             .addwrap    => try sema.zirArithmetic(block, inst, .addwrap,    true),
   1332             .add_sat    => try sema.zirArithmetic(block, inst, .add_sat,    true),
   1333             .add_unsafe => try sema.zirArithmetic(block, inst, .add_unsafe, false),
   1334             .mul        => try sema.zirArithmetic(block, inst, .mul,        true),
   1335             .mulwrap    => try sema.zirArithmetic(block, inst, .mulwrap,    true),
   1336             .mul_sat    => try sema.zirArithmetic(block, inst, .mul_sat,    true),
   1337             .sub        => try sema.zirArithmetic(block, inst, .sub,        true),
   1338             .subwrap    => try sema.zirArithmetic(block, inst, .subwrap,    true),
   1339             .sub_sat    => try sema.zirArithmetic(block, inst, .sub_sat,    true),
   1340 
   1341             .div       => try sema.zirDiv(block, inst),
   1342             .div_exact => try sema.zirDivExact(block, inst),
   1343             .div_floor => try sema.zirDivFloor(block, inst),
   1344             .div_trunc => try sema.zirDivTrunc(block, inst),
   1345 
   1346             .mod_rem => try sema.zirModRem(block, inst),
   1347             .mod     => try sema.zirMod(block, inst),
   1348             .rem     => try sema.zirRem(block, inst),
   1349 
   1350             .max => try sema.zirMinMax(block, inst, .max),
   1351             .min => try sema.zirMinMax(block, inst, .min),
   1352 
   1353             .shl       => try sema.zirShl(block, inst, .shl),
   1354             .shl_exact => try sema.zirShl(block, inst, .shl_exact),
   1355             .shl_sat   => try sema.zirShl(block, inst, .shl_sat),
   1356 
   1357             .ret_ptr  => try sema.zirRetPtr(block, inst),
   1358             .ret_type => Air.internedToRef(sema.fn_ret_ty.toIntern()),
   1359 
   1360             // Instructions that we know to *always* be noreturn based solely on their tag.
   1361             // These functions match the return type of analyzeBody so that we can
   1362             // tail call them here.
   1363             .compile_error  => break try sema.zirCompileError(block, inst),
   1364             .ret_implicit   => break try sema.zirRetImplicit(block, inst),
   1365             .ret_node       => break try sema.zirRetNode(block, inst),
   1366             .ret_load       => break try sema.zirRetLoad(block, inst),
   1367             .ret_err_value  => break try sema.zirRetErrValue(block, inst),
   1368             .@"unreachable" => break try sema.zirUnreachable(block, inst),
   1369             .panic          => break try sema.zirPanic(block, inst),
   1370             .trap           => break try sema.zirTrap(block, inst),
   1371             // zig fmt: on
   1372 
   1373             // This instruction never exists in an analyzed body. It exists only in the declaration
   1374             // list for a container type.
   1375             .declaration => unreachable,
   1376 
   1377             .extended => ext: {
   1378                 const extended = datas[@intFromEnum(inst)].extended;
   1379                 break :ext switch (extended.opcode) {
   1380                     // zig fmt: off
   1381                     .struct_decl        => try sema.zirStructDecl(        block, extended, inst),
   1382                     .enum_decl          => try sema.zirEnumDecl(          block, extended, inst),
   1383                     .union_decl         => try sema.zirUnionDecl(         block, extended, inst),
   1384                     .opaque_decl        => try sema.zirOpaqueDecl(        block, extended, inst),
   1385                     .tuple_decl         => try sema.zirTupleDecl(         block, extended),
   1386                     .this               => try sema.zirThis(              block, extended),
   1387                     .ret_addr           => try sema.zirRetAddr(           block, extended),
   1388                     .builtin_src        => try sema.zirBuiltinSrc(        block, extended),
   1389                     .error_return_trace => try sema.zirErrorReturnTrace(  block),
   1390                     .frame              => try sema.zirFrame(             block, extended),
   1391                     .frame_address      => try sema.zirFrameAddress(      block, extended),
   1392                     .alloc              => try sema.zirAllocExtended(     block, extended),
   1393                     .builtin_extern     => try sema.zirBuiltinExtern(     block, extended),
   1394                     .@"asm"             => try sema.zirAsm(               block, extended, false),
   1395                     .asm_expr           => try sema.zirAsm(               block, extended, true),
   1396                     .typeof_peer        => try sema.zirTypeofPeer(        block, extended, inst),
   1397                     .compile_log        => try sema.zirCompileLog(        block, extended),
   1398                     .min_multi          => try sema.zirMinMaxMulti(       block, extended, .min),
   1399                     .max_multi          => try sema.zirMinMaxMulti(       block, extended, .max),
   1400                     .add_with_overflow  => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1401                     .sub_with_overflow  => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1402                     .mul_with_overflow  => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1403                     .shl_with_overflow  => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
   1404                     .c_undef            => try sema.zirCUndef(            block, extended),
   1405                     .c_include          => try sema.zirCInclude(          block, extended),
   1406                     .c_define           => try sema.zirCDefine(           block, extended),
   1407                     .wasm_memory_size   => try sema.zirWasmMemorySize(    block, extended),
   1408                     .wasm_memory_grow   => try sema.zirWasmMemoryGrow(    block, extended),
   1409                     .prefetch           => try sema.zirPrefetch(          block, extended),
   1410                     .error_cast         => try sema.zirErrorCast(         block, extended),
   1411                     .select             => try sema.zirSelect(            block, extended),
   1412                     .int_from_error     => try sema.zirIntFromError(      block, extended),
   1413                     .error_from_int     => try sema.zirErrorFromInt(      block, extended),
   1414                     .reify              => try sema.zirReify(             block, extended, inst),
   1415                     .cmpxchg            => try sema.zirCmpxchg(           block, extended),
   1416                     .c_va_arg           => try sema.zirCVaArg(            block, extended),
   1417                     .c_va_copy          => try sema.zirCVaCopy(           block, extended),
   1418                     .c_va_end           => try sema.zirCVaEnd(            block, extended),
   1419                     .c_va_start         => try sema.zirCVaStart(          block, extended),
   1420                     .ptr_cast_full      => try sema.zirPtrCastFull(       block, extended),
   1421                     .ptr_cast_no_dest   => try sema.zirPtrCastNoDest(     block, extended),
   1422                     .work_item_id       => try sema.zirWorkItem(          block, extended, extended.opcode),
   1423                     .work_group_size    => try sema.zirWorkItem(          block, extended, extended.opcode),
   1424                     .work_group_id      => try sema.zirWorkItem(          block, extended, extended.opcode),
   1425                     .in_comptime        => try sema.zirInComptime(        block),
   1426                     .closure_get        => try sema.zirClosureGet(        block, extended),
   1427                     // zig fmt: on
   1428 
   1429                     .set_float_mode => {
   1430                         try sema.zirSetFloatMode(block, extended);
   1431                         i += 1;
   1432                         continue;
   1433                     },
   1434                     .breakpoint => {
   1435                         if (!block.isComptime()) {
   1436                             _ = try block.addNoOp(.breakpoint);
   1437                         }
   1438                         i += 1;
   1439                         continue;
   1440                     },
   1441                     .disable_instrumentation => {
   1442                         try sema.zirDisableInstrumentation();
   1443                         i += 1;
   1444                         continue;
   1445                     },
   1446                     .disable_intrinsics => {
   1447                         try sema.zirDisableIntrinsics();
   1448                         i += 1;
   1449                         continue;
   1450                     },
   1451                     .restore_err_ret_index => {
   1452                         try sema.zirRestoreErrRetIndex(block, extended);
   1453                         i += 1;
   1454                         continue;
   1455                     },
   1456                     .branch_hint => {
   1457                         try sema.zirBranchHint(block, extended);
   1458                         i += 1;
   1459                         continue;
   1460                     },
   1461                     .value_placeholder => unreachable, // never appears in a body
   1462                     .field_parent_ptr => try sema.zirFieldParentPtr(block, extended),
   1463                     .builtin_value => try sema.zirBuiltinValue(block, extended),
   1464                     .inplace_arith_result_ty => try sema.zirInplaceArithResultTy(extended),
   1465                     .dbg_empty_stmt => {
   1466                         try sema.zirDbgEmptyStmt(block, inst);
   1467                         i += 1;
   1468                         continue;
   1469                     },
   1470                     .astgen_error => return error.AnalysisFail,
   1471                     .float_op_result_ty => try sema.zirFloatOpResultType(block, extended),
   1472                 };
   1473             },
   1474 
   1475             // Instructions that we know can *never* be noreturn based solely on
   1476             // their tag. We avoid needlessly checking if they are noreturn and
   1477             // continue the loop.
   1478             // We also know that they cannot be referenced later, so we avoid
   1479             // putting them into the map.
   1480             .dbg_stmt => {
   1481                 try sema.zirDbgStmt(block, inst);
   1482                 i += 1;
   1483                 continue;
   1484             },
   1485             .dbg_var_ptr => {
   1486                 try sema.zirDbgVar(block, inst, .dbg_var_ptr);
   1487                 i += 1;
   1488                 continue;
   1489             },
   1490             .dbg_var_val => {
   1491                 try sema.zirDbgVar(block, inst, .dbg_var_val);
   1492                 i += 1;
   1493                 continue;
   1494             },
   1495             .ensure_err_union_payload_void => {
   1496                 try sema.zirEnsureErrUnionPayloadVoid(block, inst);
   1497                 i += 1;
   1498                 continue;
   1499             },
   1500             .ensure_result_non_error => {
   1501                 try sema.zirEnsureResultNonError(block, inst);
   1502                 i += 1;
   1503                 continue;
   1504             },
   1505             .ensure_result_used => {
   1506                 try sema.zirEnsureResultUsed(block, inst);
   1507                 i += 1;
   1508                 continue;
   1509             },
   1510             .set_eval_branch_quota => {
   1511                 try sema.zirSetEvalBranchQuota(block, inst);
   1512                 i += 1;
   1513                 continue;
   1514             },
   1515             .atomic_store => {
   1516                 try sema.zirAtomicStore(block, inst);
   1517                 i += 1;
   1518                 continue;
   1519             },
   1520             .store_node => {
   1521                 try sema.zirStoreNode(block, inst);
   1522                 i += 1;
   1523                 continue;
   1524             },
   1525             .store_to_inferred_ptr => {
   1526                 try sema.zirStoreToInferredPtr(block, inst);
   1527                 i += 1;
   1528                 continue;
   1529             },
   1530             .validate_struct_init_ty => {
   1531                 try sema.zirValidateStructInitTy(block, inst, false);
   1532                 i += 1;
   1533                 continue;
   1534             },
   1535             .validate_struct_init_result_ty => {
   1536                 try sema.zirValidateStructInitTy(block, inst, true);
   1537                 i += 1;
   1538                 continue;
   1539             },
   1540             .validate_array_init_ty => {
   1541                 try sema.zirValidateArrayInitTy(block, inst, false);
   1542                 i += 1;
   1543                 continue;
   1544             },
   1545             .validate_array_init_result_ty => {
   1546                 try sema.zirValidateArrayInitTy(block, inst, true);
   1547                 i += 1;
   1548                 continue;
   1549             },
   1550             .validate_ptr_struct_init => {
   1551                 try sema.zirValidatePtrStructInit(block, inst);
   1552                 i += 1;
   1553                 continue;
   1554             },
   1555             .validate_ptr_array_init => {
   1556                 try sema.zirValidatePtrArrayInit(block, inst);
   1557                 i += 1;
   1558                 continue;
   1559             },
   1560             .validate_deref => {
   1561                 try sema.zirValidateDeref(block, inst);
   1562                 i += 1;
   1563                 continue;
   1564             },
   1565             .validate_destructure => {
   1566                 try sema.zirValidateDestructure(block, inst);
   1567                 i += 1;
   1568                 continue;
   1569             },
   1570             .validate_ref_ty => {
   1571                 try sema.zirValidateRefTy(block, inst);
   1572                 i += 1;
   1573                 continue;
   1574             },
   1575             .validate_const => {
   1576                 try sema.zirValidateConst(block, inst);
   1577                 i += 1;
   1578                 continue;
   1579             },
   1580             .@"export" => {
   1581                 try sema.zirExport(block, inst);
   1582                 i += 1;
   1583                 continue;
   1584             },
   1585             .set_runtime_safety => {
   1586                 try sema.zirSetRuntimeSafety(block, inst);
   1587                 i += 1;
   1588                 continue;
   1589             },
   1590             .param => {
   1591                 try sema.zirParam(block, inst, false);
   1592                 i += 1;
   1593                 continue;
   1594             },
   1595             .param_comptime => {
   1596                 try sema.zirParam(block, inst, true);
   1597                 i += 1;
   1598                 continue;
   1599             },
   1600             .param_anytype => {
   1601                 try sema.zirParamAnytype(block, inst, false);
   1602                 i += 1;
   1603                 continue;
   1604             },
   1605             .param_anytype_comptime => {
   1606                 try sema.zirParamAnytype(block, inst, true);
   1607                 i += 1;
   1608                 continue;
   1609             },
   1610             .memcpy => {
   1611                 try sema.zirMemcpy(block, inst, .memcpy, true);
   1612                 i += 1;
   1613                 continue;
   1614             },
   1615             .memmove => {
   1616                 try sema.zirMemcpy(block, inst, .memmove, false);
   1617                 i += 1;
   1618                 continue;
   1619             },
   1620             .memset => {
   1621                 try sema.zirMemset(block, inst);
   1622                 i += 1;
   1623                 continue;
   1624             },
   1625             .check_comptime_control_flow => {
   1626                 if (!block.isComptime()) {
   1627                     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   1628                     const src = block.nodeOffset(inst_data.src_node);
   1629                     const inline_block = inst_data.operand.toIndex().?;
   1630 
   1631                     var check_block = block;
   1632                     const target_runtime_index = while (true) {
   1633                         if (check_block.inline_block == inline_block.toOptional()) {
   1634                             break check_block.runtime_index;
   1635                         }
   1636                         check_block = check_block.parent.?;
   1637                     };
   1638 
   1639                     if (@intFromEnum(target_runtime_index) < @intFromEnum(block.runtime_index)) {
   1640                         const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
   1641                         const msg = msg: {
   1642                             const msg = try sema.errMsg(src, "comptime control flow inside runtime block", .{});
   1643                             errdefer msg.destroy(sema.gpa);
   1644                             try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
   1645                             break :msg msg;
   1646                         };
   1647                         return sema.failWithOwnedErrorMsg(block, msg);
   1648                     }
   1649                 }
   1650                 i += 1;
   1651                 continue;
   1652             },
   1653             .save_err_ret_index => {
   1654                 try sema.zirSaveErrRetIndex(block, inst);
   1655                 i += 1;
   1656                 continue;
   1657             },
   1658             .restore_err_ret_index_unconditional => {
   1659                 const un_node = datas[@intFromEnum(inst)].un_node;
   1660                 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), un_node.operand, .none);
   1661                 i += 1;
   1662                 continue;
   1663             },
   1664             .restore_err_ret_index_fn_entry => {
   1665                 const un_node = datas[@intFromEnum(inst)].un_node;
   1666                 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), .none, un_node.operand);
   1667                 i += 1;
   1668                 continue;
   1669             },
   1670 
   1671             // Special case instructions to handle comptime control flow.
   1672             .@"break" => {
   1673                 if (block.isComptime()) {
   1674                     sema.comptime_break_inst = inst;
   1675                     return error.ComptimeBreak;
   1676                 } else {
   1677                     try sema.zirBreak(block, inst);
   1678                     break;
   1679                 }
   1680             },
   1681             .break_inline => {
   1682                 sema.comptime_break_inst = inst;
   1683                 return error.ComptimeBreak;
   1684             },
   1685             .repeat => {
   1686                 if (block.isComptime()) {
   1687                     // Send comptime control flow back to the beginning of this block.
   1688                     const src = block.nodeOffset(datas[@intFromEnum(inst)].node);
   1689                     try sema.emitBackwardBranch(block, src);
   1690                     i = 0;
   1691                     continue;
   1692                 } else {
   1693                     // We are definitely called by `zirLoop`, which will treat the
   1694                     // fact that this body does not terminate `noreturn` as an
   1695                     // implicit repeat.
   1696                     // TODO: since AIR has `repeat` now, we could change ZIR to generate
   1697                     // more optimal code utilizing `repeat` instructions across blocks!
   1698                     break;
   1699                 }
   1700             },
   1701             .repeat_inline => {
   1702                 // Send comptime control flow back to the beginning of this block.
   1703                 const src = block.nodeOffset(datas[@intFromEnum(inst)].node);
   1704                 try sema.emitBackwardBranch(block, src);
   1705                 i = 0;
   1706                 continue;
   1707             },
   1708             .switch_continue => if (block.isComptime()) {
   1709                 sema.comptime_break_inst = inst;
   1710                 return error.ComptimeBreak;
   1711             } else {
   1712                 try sema.zirSwitchContinue(block, inst);
   1713                 break;
   1714             },
   1715 
   1716             .loop => if (block.isComptime()) {
   1717                 continue :inst .block_inline;
   1718             } else try sema.zirLoop(block, inst),
   1719 
   1720             .block => if (block.isComptime()) {
   1721                 continue :inst .block_inline;
   1722             } else try sema.zirBlock(block, inst),
   1723 
   1724             .block_comptime => {
   1725                 const pl_node = datas[@intFromEnum(inst)].pl_node;
   1726                 const src = block.nodeOffset(pl_node.src_node);
   1727                 const extra = sema.code.extraData(Zir.Inst.BlockComptime, pl_node.payload_index);
   1728                 const block_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1729 
   1730                 var child_block = block.makeSubBlock();
   1731                 defer child_block.instructions.deinit(sema.gpa);
   1732 
   1733                 // We won't have any merges, but we must ensure this block is properly labeled for
   1734                 // any `.restore_err_ret_index_*` instructions.
   1735                 var label: Block.Label = .{
   1736                     .zir_block = inst,
   1737                     .merges = undefined,
   1738                 };
   1739                 child_block.label = &label;
   1740 
   1741                 child_block.comptime_reason = .{ .reason = .{
   1742                     .src = src,
   1743                     .r = .{ .simple = extra.data.reason },
   1744                 } };
   1745 
   1746                 const result = try sema.resolveInlineBody(&child_block, block_body, inst);
   1747 
   1748                 // Only check for the result being comptime-known in the outermost `block_comptime`.
   1749                 // That way, AstGen can safely elide redundant `block_comptime` without affecting semantics.
   1750                 if (!block.isComptime() and !try sema.isComptimeKnown(result)) {
   1751                     return sema.failWithNeededComptime(&child_block, src, null);
   1752                 }
   1753 
   1754                 break :inst result;
   1755             },
   1756 
   1757             .block_inline => blk: {
   1758                 // Directly analyze the block body without introducing a new block.
   1759                 // However, in the case of a corresponding break_inline which reaches
   1760                 // through a runtime conditional branch, we must retroactively emit
   1761                 // a block, so we remember the block index here just in case.
   1762                 const block_index = block.instructions.items.len;
   1763                 const inst_data = datas[@intFromEnum(inst)].pl_node;
   1764                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   1765                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1766                 const gpa = sema.gpa;
   1767 
   1768                 const BreakResult = struct {
   1769                     block_inst: Zir.Inst.Index,
   1770                     operand: Zir.Inst.Ref,
   1771                 };
   1772 
   1773                 const opt_break_data: ?BreakResult, const need_debug_scope = b: {
   1774                     // Create a temporary child block so that this inline block is properly
   1775                     // labeled for any .restore_err_ret_index instructions
   1776                     var child_block = block.makeSubBlock();
   1777                     var need_debug_scope = false;
   1778                     child_block.need_debug_scope = &need_debug_scope;
   1779 
   1780                     // If this block contains a function prototype, we need to reset the
   1781                     // current list of parameters and restore it later.
   1782                     // Note: this probably needs to be resolved in a more general manner.
   1783                     const tag_index = @intFromEnum(inline_body[inline_body.len - 1]);
   1784                     child_block.inline_block = (if (tags[tag_index] == .repeat_inline)
   1785                         inline_body[0]
   1786                     else
   1787                         inst).toOptional();
   1788 
   1789                     var label: Block.Label = .{
   1790                         .zir_block = inst,
   1791                         .merges = undefined,
   1792                     };
   1793                     child_block.label = &label;
   1794 
   1795                     // Write these instructions directly into the parent block
   1796                     child_block.instructions = block.instructions;
   1797                     defer block.instructions = child_block.instructions;
   1798 
   1799                     const break_result: ?BreakResult = if (sema.analyzeBodyInner(&child_block, inline_body)) |_| r: {
   1800                         break :r null;
   1801                     } else |err| switch (err) {
   1802                         error.ComptimeBreak => brk_res: {
   1803                             const break_inst = sema.comptime_break_inst;
   1804                             const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
   1805                             const break_extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
   1806                             break :brk_res .{
   1807                                 .block_inst = break_extra.block_inst,
   1808                                 .operand = break_data.operand,
   1809                             };
   1810                         },
   1811                         else => |e| return e,
   1812                     };
   1813 
   1814                     if (need_debug_scope) {
   1815                         _ = try sema.ensurePostHoc(block, inst);
   1816                     }
   1817 
   1818                     break :b .{ break_result, need_debug_scope };
   1819                 };
   1820 
   1821                 // A runtime conditional branch that needs a post-hoc block to be
   1822                 // emitted communicates this by mapping the block index into the inst map.
   1823                 if (map.get(inst)) |new_block_ref| ph: {
   1824                     // Comptime control flow populates the map, so we don't actually know
   1825                     // if this is a post-hoc runtime block until we check the
   1826                     // post_hoc_block map.
   1827                     const new_block_inst = new_block_ref.toIndex() orelse break :ph;
   1828                     const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse
   1829                         break :ph;
   1830 
   1831                     // In this case we need to move all the instructions starting at
   1832                     // block_index from the current block into this new one.
   1833 
   1834                     if (opt_break_data) |break_data| {
   1835                         // This is a comptime break which we now change to a runtime break
   1836                         // since it crosses a runtime branch.
   1837                         // It may pass through our currently being analyzed block_inline or it
   1838                         // may point directly to it. In the latter case, this modifies the
   1839                         // block that we looked up in the post_hoc_blocks map above.
   1840                         try sema.addRuntimeBreak(block, break_data.block_inst, break_data.operand);
   1841                     }
   1842 
   1843                     try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]);
   1844                     block.instructions.items.len = block_index;
   1845 
   1846                     const block_result = try sema.resolveAnalyzedBlock(block, block.nodeOffset(inst_data.src_node), &labeled_block.block, &labeled_block.label.merges, need_debug_scope);
   1847                     {
   1848                         // Destroy the ad-hoc block entry so that it does not interfere with
   1849                         // the next iteration of comptime control flow, if any.
   1850                         labeled_block.destroy(gpa);
   1851                         assert(sema.post_hoc_blocks.remove(new_block_inst));
   1852                     }
   1853 
   1854                     break :blk block_result;
   1855                 }
   1856 
   1857                 const break_data = opt_break_data orelse break;
   1858                 if (inst == break_data.block_inst) {
   1859                     break :blk try sema.resolveInst(break_data.operand);
   1860                 } else {
   1861                     // `comptime_break_inst` preserved from `analyzeBodyInner` above.
   1862                     return error.ComptimeBreak;
   1863                 }
   1864             },
   1865             .condbr => if (block.isComptime()) {
   1866                 continue :inst .condbr_inline;
   1867             } else {
   1868                 try sema.zirCondbr(block, inst);
   1869                 break;
   1870             },
   1871             .condbr_inline => {
   1872                 const inst_data = datas[@intFromEnum(inst)].pl_node;
   1873                 const cond_src = block.src(.{ .node_offset_if_cond = inst_data.src_node });
   1874                 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
   1875                 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
   1876                 const else_body = sema.code.bodySlice(
   1877                     extra.end + then_body.len,
   1878                     extra.data.else_body_len,
   1879                 );
   1880                 const uncasted_cond = try sema.resolveInst(extra.data.condition);
   1881                 const cond = try sema.coerce(block, .bool, uncasted_cond, cond_src);
   1882                 const cond_val = try sema.resolveConstDefinedValue(
   1883                     block,
   1884                     cond_src,
   1885                     cond,
   1886                     // If this block is comptime, it's more helpful to just give the outer message.
   1887                     // This is particularly true if this came from a comptime `condbr` above.
   1888                     if (block.isComptime()) null else .{ .simple = .inline_loop_operand },
   1889                 );
   1890                 const inline_body = if (cond_val.toBool()) then_body else else_body;
   1891 
   1892                 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
   1893                 const old_runtime_index = block.runtime_index;
   1894                 defer block.runtime_index = old_runtime_index;
   1895 
   1896                 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
   1897                 break :inst result;
   1898             },
   1899             .@"try" => blk: {
   1900                 if (!block.isComptime()) break :blk try sema.zirTry(block, inst);
   1901                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   1902                 const src = block.nodeOffset(inst_data.src_node);
   1903                 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node });
   1904                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
   1905                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1906                 const err_union = try sema.resolveInst(extra.data.operand);
   1907                 const err_union_ty = sema.typeOf(err_union);
   1908                 if (err_union_ty.zigTypeTag(zcu) != .error_union) {
   1909                     return sema.failWithOwnedErrorMsg(block, msg: {
   1910                         const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
   1911                         errdefer msg.destroy(sema.gpa);
   1912                         try sema.addDeclaredHereNote(msg, err_union_ty);
   1913                         try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
   1914                         break :msg msg;
   1915                     });
   1916                 }
   1917                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
   1918                 assert(is_non_err != .none);
   1919                 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
   1920                 if (is_non_err_val.toBool()) {
   1921                     break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
   1922                 }
   1923                 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
   1924                 break :blk result;
   1925             },
   1926             .try_ptr => blk: {
   1927                 if (!block.isComptime()) break :blk try sema.zirTryPtr(block, inst);
   1928                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   1929                 const src = block.nodeOffset(inst_data.src_node);
   1930                 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node });
   1931                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
   1932                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1933                 const operand = try sema.resolveInst(extra.data.operand);
   1934                 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
   1935                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
   1936                 assert(is_non_err != .none);
   1937                 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
   1938                 if (is_non_err_val.toBool()) {
   1939                     break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
   1940                 }
   1941                 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
   1942                 break :blk result;
   1943             },
   1944             .@"defer" => blk: {
   1945                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"defer";
   1946                 const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len);
   1947                 if (sema.analyzeBodyInner(block, defer_body)) |_| {
   1948                     // The defer terminated noreturn - no more analysis needed.
   1949                     break;
   1950                 } else |err| switch (err) {
   1951                     error.ComptimeBreak => {},
   1952                     else => |e| return e,
   1953                 }
   1954                 if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) {
   1955                     return error.ComptimeBreak;
   1956                 }
   1957                 break :blk .void_value;
   1958             },
   1959             .defer_err_code => blk: {
   1960                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].defer_err_code;
   1961                 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
   1962                 const defer_body = sema.code.bodySlice(extra.index, extra.len);
   1963                 const err_code = try sema.resolveInst(inst_data.err_code);
   1964                 try map.ensureSpaceForInstructions(sema.gpa, defer_body);
   1965                 map.putAssumeCapacity(extra.remapped_err_code, err_code);
   1966                 if (sema.analyzeBodyInner(block, defer_body)) |_| {
   1967                     // The defer terminated noreturn - no more analysis needed.
   1968                     break;
   1969                 } else |err| switch (err) {
   1970                     error.ComptimeBreak => {},
   1971                     else => |e| return e,
   1972                 }
   1973                 if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) {
   1974                     return error.ComptimeBreak;
   1975                 }
   1976                 break :blk .void_value;
   1977             },
   1978         };
   1979         if (sema.isNoReturn(air_inst)) {
   1980             // We're going to assume that the body itself is noreturn, so let's ensure that now
   1981             assert(block.instructions.items.len > 0);
   1982             assert(sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef()));
   1983             break;
   1984         }
   1985         map.putAssumeCapacity(inst, air_inst);
   1986         i += 1;
   1987     }
   1988 }
   1989 
   1990 pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
   1991     if (zir_ref == .none) {
   1992         return .none;
   1993     } else {
   1994         return resolveInst(sema, zir_ref);
   1995     }
   1996 }
   1997 
   1998 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
   1999     assert(zir_ref != .none);
   2000     if (zir_ref.toIndex()) |i| {
   2001         return sema.inst_map.get(i).?;
   2002     }
   2003     // First section of indexes correspond to a set number of constant values.
   2004     // We intentionally map the same indexes to the same values between ZIR and AIR.
   2005     return @enumFromInt(@intFromEnum(zir_ref));
   2006 }
   2007 
   2008 fn resolveConstBool(
   2009     sema: *Sema,
   2010     block: *Block,
   2011     src: LazySrcLoc,
   2012     zir_ref: Zir.Inst.Ref,
   2013     reason: ComptimeReason,
   2014 ) !bool {
   2015     const air_inst = try sema.resolveInst(zir_ref);
   2016     const wanted_type: Type = .bool;
   2017     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   2018     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
   2019     return val.toBool();
   2020 }
   2021 
   2022 fn resolveConstString(
   2023     sema: *Sema,
   2024     block: *Block,
   2025     src: LazySrcLoc,
   2026     zir_ref: Zir.Inst.Ref,
   2027     reason: ComptimeReason,
   2028 ) ![]u8 {
   2029     const air_inst = try sema.resolveInst(zir_ref);
   2030     return sema.toConstString(block, src, air_inst, reason);
   2031 }
   2032 
   2033 pub fn toConstString(
   2034     sema: *Sema,
   2035     block: *Block,
   2036     src: LazySrcLoc,
   2037     air_inst: Air.Inst.Ref,
   2038     reason: ComptimeReason,
   2039 ) ![]u8 {
   2040     const pt = sema.pt;
   2041     const coerced_inst = try sema.coerce(block, .slice_const_u8, air_inst, src);
   2042     const slice_val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
   2043     const arr_val = try sema.derefSliceAsArray(block, src, slice_val, reason);
   2044     return arr_val.toAllocatedBytes(arr_val.typeOf(pt.zcu), sema.arena, pt);
   2045 }
   2046 
   2047 pub fn resolveConstStringIntern(
   2048     sema: *Sema,
   2049     block: *Block,
   2050     src: LazySrcLoc,
   2051     zir_ref: Zir.Inst.Ref,
   2052     reason: ComptimeReason,
   2053 ) !InternPool.NullTerminatedString {
   2054     const air_inst = try sema.resolveInst(zir_ref);
   2055     const wanted_type: Type = .slice_const_u8;
   2056     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   2057     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
   2058     return sema.sliceToIpString(block, src, val, reason);
   2059 }
   2060 
   2061 fn resolveTypeOrPoison(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !?Type {
   2062     const air_inst = try sema.resolveInst(zir_ref);
   2063     const ty = try sema.analyzeAsType(block, src, air_inst);
   2064     if (ty.isGenericPoison()) return null;
   2065     return ty;
   2066 }
   2067 
   2068 pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
   2069     return (try sema.resolveTypeOrPoison(block, src, zir_ref)).?;
   2070 }
   2071 
   2072 fn resolveDestType(
   2073     sema: *Sema,
   2074     block: *Block,
   2075     src: LazySrcLoc,
   2076     zir_ref: Zir.Inst.Ref,
   2077     strat: enum { remove_eu_opt, remove_eu, remove_opt },
   2078     builtin_name: []const u8,
   2079 ) !Type {
   2080     const pt = sema.pt;
   2081     const zcu = pt.zcu;
   2082     const remove_eu = switch (strat) {
   2083         .remove_eu_opt, .remove_eu => true,
   2084         .remove_opt => false,
   2085     };
   2086     const remove_opt = switch (strat) {
   2087         .remove_eu_opt, .remove_opt => true,
   2088         .remove_eu => false,
   2089     };
   2090 
   2091     const raw_ty = try sema.resolveTypeOrPoison(block, src, zir_ref) orelse {
   2092         // Cast builtins use their result type as the destination type, but
   2093         // it could be an anytype argument, which we can't catch in AstGen.
   2094         const msg = msg: {
   2095             const msg = try sema.errMsg(src, "{s} must have a known result type", .{builtin_name});
   2096             errdefer msg.destroy(sema.gpa);
   2097             switch (sema.genericPoisonReason(block, zir_ref)) {
   2098                 .anytype_param => |call_src| try sema.errNote(call_src, msg, "result type is unknown due to anytype parameter", .{}),
   2099                 .anyopaque_ptr => |ptr_src| try sema.errNote(ptr_src, msg, "result type is unknown due to opaque pointer type", .{}),
   2100                 .unknown => {},
   2101             }
   2102             try sema.errNote(src, msg, "use @as to provide explicit result type", .{});
   2103             break :msg msg;
   2104         };
   2105         return sema.failWithOwnedErrorMsg(block, msg);
   2106     };
   2107 
   2108     if (remove_eu and raw_ty.zigTypeTag(zcu) == .error_union) {
   2109         const eu_child = raw_ty.errorUnionPayload(zcu);
   2110         if (remove_opt and eu_child.zigTypeTag(zcu) == .optional) {
   2111             return eu_child.childType(zcu);
   2112         }
   2113         return eu_child;
   2114     }
   2115     if (remove_opt and raw_ty.zigTypeTag(zcu) == .optional) {
   2116         return raw_ty.childType(zcu);
   2117     }
   2118     return raw_ty;
   2119 }
   2120 
   2121 const GenericPoisonReason = union(enum) {
   2122     anytype_param: LazySrcLoc,
   2123     anyopaque_ptr: LazySrcLoc,
   2124     unknown,
   2125 };
   2126 
   2127 /// Backtracks through ZIR instructions to determine the reason a generic poison
   2128 /// type was created. Used for error reporting.
   2129 fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoisonReason {
   2130     var cur = ref;
   2131     while (true) {
   2132         const inst = cur.toIndex() orelse return .unknown;
   2133         switch (sema.code.instructions.items(.tag)[@intFromEnum(inst)]) {
   2134             .validate_array_init_ref_ty => {
   2135                 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   2136                 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
   2137                 cur = extra.ptr_ty;
   2138             },
   2139             .array_init_elem_type => {
   2140                 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
   2141                 cur = bin.lhs;
   2142             },
   2143             .indexable_ptr_elem_type, .splat_op_result_ty => {
   2144                 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   2145                 cur = un_node.operand;
   2146             },
   2147             .struct_init_field_type => {
   2148                 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   2149                 const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data;
   2150                 cur = extra.container_type;
   2151             },
   2152             .elem_type => {
   2153                 // There are two cases here: the pointer type may already have been
   2154                 // generic poison, or it may have been an anyopaque pointer.
   2155                 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   2156                 const operand_ref = try sema.resolveInst(un_node.operand);
   2157                 const operand_val = operand_ref.toInterned() orelse return .unknown;
   2158                 if (operand_val == .generic_poison_type) {
   2159                     // The pointer was generic poison - keep looking.
   2160                     cur = un_node.operand;
   2161                 } else {
   2162                     // This must be an anyopaque pointer!
   2163                     return .{ .anyopaque_ptr = block.nodeOffset(un_node.src_node) };
   2164                 }
   2165             },
   2166             .call, .field_call => {
   2167                 // A function call can never return generic poison, so we must be
   2168                 // evaluating an `anytype` function parameter.
   2169                 // TODO: better source location - function decl rather than call
   2170                 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   2171                 return .{ .anytype_param = block.nodeOffset(pl_node.src_node) };
   2172             },
   2173             else => return .unknown,
   2174         }
   2175     }
   2176 }
   2177 
   2178 fn analyzeAsType(
   2179     sema: *Sema,
   2180     block: *Block,
   2181     src: LazySrcLoc,
   2182     air_inst: Air.Inst.Ref,
   2183 ) !Type {
   2184     const wanted_type: Type = .type;
   2185     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   2186     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{ .simple = .type });
   2187     return val.toType();
   2188 }
   2189 
   2190 pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
   2191     const pt = sema.pt;
   2192     const zcu = pt.zcu;
   2193     const comp = zcu.comp;
   2194     const gpa = sema.gpa;
   2195     const ip = &zcu.intern_pool;
   2196     if (!comp.config.any_error_tracing) return;
   2197 
   2198     assert(!block.isComptime());
   2199     var err_trace_block = block.makeSubBlock();
   2200     defer err_trace_block.instructions.deinit(gpa);
   2201 
   2202     const src: LazySrcLoc = LazySrcLoc.unneeded;
   2203 
   2204     // var addrs: [err_return_trace_addr_count]usize = undefined;
   2205     const err_return_trace_addr_count = 32;
   2206     const addr_arr_ty = try pt.arrayType(.{
   2207         .len = err_return_trace_addr_count,
   2208         .child = .usize_type,
   2209     });
   2210     const addrs_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(addr_arr_ty));
   2211 
   2212     // var st: StackTrace = undefined;
   2213     const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
   2214     try stack_trace_ty.resolveFields(pt);
   2215     const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty));
   2216 
   2217     // st.instruction_addresses = &addrs;
   2218     const instruction_addresses_field_name = try ip.getOrPutString(gpa, pt.tid, "instruction_addresses", .no_embedded_nulls);
   2219     const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true);
   2220     try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
   2221 
   2222     // st.index = 0;
   2223     const index_field_name = try ip.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
   2224     const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true);
   2225     try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store);
   2226 
   2227     // @errorReturnTrace() = &st;
   2228     _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
   2229 
   2230     try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items);
   2231 }
   2232 
   2233 /// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value.
   2234 fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
   2235     const zcu = sema.pt.zcu;
   2236     assert(inst != .none);
   2237 
   2238     if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| {
   2239         return opv;
   2240     }
   2241 
   2242     if (inst.toInterned()) |ip_index| {
   2243         const val: Value = .fromInterned(ip_index);
   2244         assert(val.getVariable(zcu) == null);
   2245         return val;
   2246     } else {
   2247         // Runtime-known value.
   2248         const air_tags = sema.air_instructions.items(.tag);
   2249         switch (air_tags[@intFromEnum(inst.toIndex().?)]) {
   2250             .inferred_alloc => unreachable, // assertion failure
   2251             .inferred_alloc_comptime => unreachable, // assertion failure
   2252             else => {},
   2253         }
   2254         return null;
   2255     }
   2256 }
   2257 
   2258 /// Like `resolveValue`, but emits an error if the value is not comptime-known.
   2259 fn resolveConstValue(
   2260     sema: *Sema,
   2261     block: *Block,
   2262     src: LazySrcLoc,
   2263     inst: Air.Inst.Ref,
   2264     reason: ?ComptimeReason,
   2265 ) CompileError!Value {
   2266     return try sema.resolveValue(inst) orelse {
   2267         return sema.failWithNeededComptime(block, src, reason);
   2268     };
   2269 }
   2270 
   2271 /// Like `resolveValue`, but emits an error if the value is comptime-known to be undefined.
   2272 fn resolveDefinedValue(
   2273     sema: *Sema,
   2274     block: *Block,
   2275     src: LazySrcLoc,
   2276     air_ref: Air.Inst.Ref,
   2277 ) CompileError!?Value {
   2278     const pt = sema.pt;
   2279     const zcu = pt.zcu;
   2280     const val = try sema.resolveValue(air_ref) orelse return null;
   2281     if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null);
   2282     return val;
   2283 }
   2284 
   2285 /// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined.
   2286 fn resolveConstDefinedValue(
   2287     sema: *Sema,
   2288     block: *Block,
   2289     src: LazySrcLoc,
   2290     air_ref: Air.Inst.Ref,
   2291     reason: ?ComptimeReason,
   2292 ) CompileError!Value {
   2293     const val = try sema.resolveConstValue(block, src, air_ref, reason);
   2294     if (val.isUndef(sema.pt.zcu)) return sema.failWithUseOfUndef(block, src, null);
   2295     return val;
   2296 }
   2297 
   2298 /// Like `resolveValue`, but recursively resolves lazy values before returning.
   2299 fn resolveValueResolveLazy(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
   2300     return try sema.resolveLazyValue((try sema.resolveValue(inst)) orelse return null);
   2301 }
   2302 
   2303 /// Value Tag may be `undef` or `variable`.
   2304 pub fn resolveFinalDeclValue(
   2305     sema: *Sema,
   2306     block: *Block,
   2307     src: LazySrcLoc,
   2308     air_ref: Air.Inst.Ref,
   2309 ) CompileError!Value {
   2310     const zcu = sema.pt.zcu;
   2311     const val = try sema.resolveConstValue(block, src, air_ref, .{ .simple = .container_var_init });
   2312     if (val.canMutateComptimeVarState(zcu)) {
   2313         const ip = &zcu.intern_pool;
   2314         const nav = ip.getNav(sema.owner.unwrap().nav_val);
   2315         return sema.failWithContainsReferenceToComptimeVar(block, src, nav.name, "global variable", val);
   2316     }
   2317     return val;
   2318 }
   2319 
   2320 fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: ?ComptimeReason) CompileError {
   2321     const msg, const fail_block = msg: {
   2322         const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
   2323         errdefer msg.destroy(sema.gpa);
   2324         const fail_block = if (reason) |r| b: {
   2325             try r.explain(sema, src, msg);
   2326             break :b block;
   2327         } else b: {
   2328             break :b try block.explainWhyBlockIsComptime(msg);
   2329         };
   2330         break :msg .{ msg, fail_block };
   2331     };
   2332     return sema.failWithOwnedErrorMsg(fail_block, msg);
   2333 }
   2334 
   2335 pub fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc, vector_index: ?usize) CompileError {
   2336     return sema.failWithOwnedErrorMsg(block, msg: {
   2337         const msg = try sema.errMsg(src, "use of undefined value here causes illegal behavior", .{});
   2338         errdefer msg.destroy(sema.gpa);
   2339         if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i});
   2340         break :msg msg;
   2341     });
   2342 }
   2343 
   2344 pub fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2345     return sema.fail(block, src, "division by zero here causes illegal behavior", .{});
   2346 }
   2347 
   2348 pub fn failWithTooLargeShiftAmount(
   2349     sema: *Sema,
   2350     block: *Block,
   2351     operand_ty: Type,
   2352     shift_amt: Value,
   2353     shift_src: LazySrcLoc,
   2354     vector_index: ?usize,
   2355 ) CompileError {
   2356     return sema.failWithOwnedErrorMsg(block, msg: {
   2357         const msg = try sema.errMsg(
   2358             shift_src,
   2359             "shift amount '{f}' is too large for operand type '{f}'",
   2360             .{ shift_amt.fmtValueSema(sema.pt, sema), operand_ty.fmt(sema.pt) },
   2361         );
   2362         errdefer msg.destroy(sema.gpa);
   2363         if (vector_index) |i| try sema.errNote(shift_src, msg, "when computing vector element at index '{d}'", .{i});
   2364         break :msg msg;
   2365     });
   2366 }
   2367 
   2368 pub fn failWithNegativeShiftAmount(sema: *Sema, block: *Block, src: LazySrcLoc, shift_amt: Value, vector_index: ?usize) CompileError {
   2369     return sema.failWithOwnedErrorMsg(block, msg: {
   2370         const msg = try sema.errMsg(src, "shift by negative amount '{f}'", .{shift_amt.fmtValueSema(sema.pt, sema)});
   2371         errdefer msg.destroy(sema.gpa);
   2372         if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i});
   2373         break :msg msg;
   2374     });
   2375 }
   2376 
   2377 pub fn failWithUnsupportedComptimeShiftAmount(sema: *Sema, block: *Block, src: LazySrcLoc, vector_index: ?usize) CompileError {
   2378     return sema.failWithOwnedErrorMsg(block, msg: {
   2379         const msg = try sema.errMsg(
   2380             src,
   2381             "this implementation only supports comptime shift amounts of up to 2^{d} - 1 bits",
   2382             .{@min(@bitSizeOf(usize), 64)},
   2383         );
   2384         errdefer msg.destroy(sema.gpa);
   2385         if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i});
   2386         break :msg msg;
   2387     });
   2388 }
   2389 
   2390 fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError {
   2391     const pt = sema.pt;
   2392     return sema.fail(block, src, "remainder division with '{f}' and '{f}': signed integers and floats must use @rem or @mod", .{
   2393         lhs_ty.fmt(pt), rhs_ty.fmt(pt),
   2394     });
   2395 }
   2396 
   2397 fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError {
   2398     const pt = sema.pt;
   2399     const msg = msg: {
   2400         const msg = try sema.errMsg(src, "expected optional type, found '{f}'", .{
   2401             non_optional_ty.fmt(pt),
   2402         });
   2403         errdefer msg.destroy(sema.gpa);
   2404         if (non_optional_ty.zigTypeTag(pt.zcu) == .error_union) {
   2405             try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   2406         }
   2407         try addDeclaredHereNote(sema, msg, non_optional_ty);
   2408         break :msg msg;
   2409     };
   2410     return sema.failWithOwnedErrorMsg(block, msg);
   2411 }
   2412 
   2413 fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
   2414     const pt = sema.pt;
   2415     const msg = msg: {
   2416         const msg = try sema.errMsg(src, "type '{f}' does not support array initialization syntax", .{
   2417             ty.fmt(pt),
   2418         });
   2419         errdefer msg.destroy(sema.gpa);
   2420         if (ty.isSlice(pt.zcu)) {
   2421             try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.elemType2(pt.zcu).fmt(pt)});
   2422         }
   2423         break :msg msg;
   2424     };
   2425     return sema.failWithOwnedErrorMsg(block, msg);
   2426 }
   2427 
   2428 fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
   2429     const pt = sema.pt;
   2430     return sema.fail(block, src, "type '{f}' does not support struct initialization syntax", .{
   2431         ty.fmt(pt),
   2432     });
   2433 }
   2434 
   2435 fn failWithErrorSetCodeMissing(
   2436     sema: *Sema,
   2437     block: *Block,
   2438     src: LazySrcLoc,
   2439     dest_err_set_ty: Type,
   2440     src_err_set_ty: Type,
   2441 ) CompileError {
   2442     const pt = sema.pt;
   2443     return sema.fail(block, src, "expected type '{f}', found type '{f}'", .{
   2444         dest_err_set_ty.fmt(pt), src_err_set_ty.fmt(pt),
   2445     });
   2446 }
   2447 
   2448 pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError {
   2449     const pt = sema.pt;
   2450     return sema.failWithOwnedErrorMsg(block, msg: {
   2451         const msg = try sema.errMsg(src, "overflow of integer type '{f}' with value '{f}'", .{
   2452             int_ty.fmt(pt), val.fmtValueSema(pt, sema),
   2453         });
   2454         errdefer msg.destroy(sema.gpa);
   2455         if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i});
   2456         break :msg msg;
   2457     });
   2458 }
   2459 
   2460 fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError {
   2461     const pt = sema.pt;
   2462     const zcu = pt.zcu;
   2463     const msg = msg: {
   2464         const msg = try sema.errMsg(init_src, "value stored in comptime field does not match the default value of the field", .{});
   2465         errdefer msg.destroy(sema.gpa);
   2466 
   2467         const struct_type = zcu.typeToStruct(container_ty) orelse break :msg msg;
   2468         try sema.errNote(.{
   2469             .base_node_inst = struct_type.zir_index,
   2470             .offset = .{ .container_field_value = @intCast(field_index) },
   2471         }, msg, "default value set here", .{});
   2472         break :msg msg;
   2473     };
   2474     return sema.failWithOwnedErrorMsg(block, msg);
   2475 }
   2476 
   2477 fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2478     const msg = msg: {
   2479         const msg = try sema.errMsg(src, "async has not been implemented in the self-hosted compiler yet", .{});
   2480         errdefer msg.destroy(sema.gpa);
   2481         break :msg msg;
   2482     };
   2483     return sema.failWithOwnedErrorMsg(block, msg);
   2484 }
   2485 
   2486 fn failWithInvalidFieldAccess(
   2487     sema: *Sema,
   2488     block: *Block,
   2489     src: LazySrcLoc,
   2490     object_ty: Type,
   2491     field_name: InternPool.NullTerminatedString,
   2492 ) CompileError {
   2493     const pt = sema.pt;
   2494     const zcu = pt.zcu;
   2495     const inner_ty = if (object_ty.isSinglePointer(zcu)) object_ty.childType(zcu) else object_ty;
   2496 
   2497     if (inner_ty.zigTypeTag(zcu) == .optional) opt: {
   2498         const child_ty = inner_ty.optionalChild(zcu);
   2499         if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :opt;
   2500         const msg = msg: {
   2501             const msg = try sema.errMsg(src, "optional type '{f}' does not support field access", .{object_ty.fmt(pt)});
   2502             errdefer msg.destroy(sema.gpa);
   2503             try sema.errNote(src, msg, "consider using '.?', 'orelse', or 'if'", .{});
   2504             break :msg msg;
   2505         };
   2506         return sema.failWithOwnedErrorMsg(block, msg);
   2507     } else if (inner_ty.zigTypeTag(zcu) == .error_union) err: {
   2508         const child_ty = inner_ty.errorUnionPayload(zcu);
   2509         if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :err;
   2510         const msg = msg: {
   2511             const msg = try sema.errMsg(src, "error union type '{f}' does not support field access", .{object_ty.fmt(pt)});
   2512             errdefer msg.destroy(sema.gpa);
   2513             try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   2514             break :msg msg;
   2515         };
   2516         return sema.failWithOwnedErrorMsg(block, msg);
   2517     }
   2518     return sema.fail(block, src, "type '{f}' does not support field access", .{object_ty.fmt(pt)});
   2519 }
   2520 
   2521 fn typeSupportsFieldAccess(zcu: *const Zcu, ty: Type, field_name: InternPool.NullTerminatedString) bool {
   2522     const ip = &zcu.intern_pool;
   2523     switch (ty.zigTypeTag(zcu)) {
   2524         .array => return field_name.eqlSlice("len", ip),
   2525         .pointer => {
   2526             const ptr_info = ty.ptrInfo(zcu);
   2527             if (ptr_info.flags.size == .slice) {
   2528                 return field_name.eqlSlice("ptr", ip) or field_name.eqlSlice("len", ip);
   2529             } else if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) {
   2530                 return field_name.eqlSlice("len", ip);
   2531             } else return false;
   2532         },
   2533         .type, .@"struct", .@"union" => return true,
   2534         else => return false,
   2535     }
   2536 }
   2537 
   2538 fn failWithComptimeErrorRetTrace(
   2539     sema: *Sema,
   2540     block: *Block,
   2541     src: LazySrcLoc,
   2542     name: InternPool.NullTerminatedString,
   2543 ) CompileError {
   2544     const pt = sema.pt;
   2545     const zcu = pt.zcu;
   2546     const msg = msg: {
   2547         const msg = try sema.errMsg(src, "caught unexpected error '{f}'", .{name.fmt(&zcu.intern_pool)});
   2548         errdefer msg.destroy(sema.gpa);
   2549 
   2550         for (sema.comptime_err_ret_trace.items) |src_loc| {
   2551             try sema.errNote(src_loc, msg, "error returned here", .{});
   2552         }
   2553         break :msg msg;
   2554     };
   2555     return sema.failWithOwnedErrorMsg(block, msg);
   2556 }
   2557 
   2558 fn failWithInvalidPtrArithmetic(sema: *Sema, block: *Block, src: LazySrcLoc, arithmetic: []const u8, supports: []const u8) CompileError {
   2559     const msg = msg: {
   2560         const msg = try sema.errMsg(src, "invalid {s} arithmetic operator", .{arithmetic});
   2561         errdefer msg.destroy(sema.gpa);
   2562         try sema.errNote(src, msg, "{s} arithmetic only supports {s}", .{ arithmetic, supports });
   2563         break :msg msg;
   2564     };
   2565     return sema.failWithOwnedErrorMsg(block, msg);
   2566 }
   2567 
   2568 /// We don't return a pointer to the new error note because the pointer
   2569 /// becomes invalid when you add another one.
   2570 pub fn errNote(
   2571     sema: *Sema,
   2572     src: LazySrcLoc,
   2573     parent: *Zcu.ErrorMsg,
   2574     comptime format: []const u8,
   2575     args: anytype,
   2576 ) error{OutOfMemory}!void {
   2577     return sema.pt.zcu.errNote(src, parent, format, args);
   2578 }
   2579 
   2580 fn addFieldErrNote(
   2581     sema: *Sema,
   2582     container_ty: Type,
   2583     field_index: usize,
   2584     parent: *Zcu.ErrorMsg,
   2585     comptime format: []const u8,
   2586     args: anytype,
   2587 ) !void {
   2588     @branchHint(.cold);
   2589     const type_src = container_ty.srcLocOrNull(sema.pt.zcu) orelse return;
   2590     const field_src: LazySrcLoc = .{
   2591         .base_node_inst = type_src.base_node_inst,
   2592         .offset = .{ .container_field_name = @intCast(field_index) },
   2593     };
   2594     try sema.errNote(field_src, parent, format, args);
   2595 }
   2596 
   2597 pub fn errMsg(
   2598     sema: *Sema,
   2599     src: LazySrcLoc,
   2600     comptime format: []const u8,
   2601     args: anytype,
   2602 ) Allocator.Error!*Zcu.ErrorMsg {
   2603     assert(src.offset != .unneeded);
   2604     return Zcu.ErrorMsg.create(sema.gpa, src, format, args);
   2605 }
   2606 
   2607 pub fn fail(
   2608     sema: *Sema,
   2609     block: *Block,
   2610     src: LazySrcLoc,
   2611     comptime format: []const u8,
   2612     args: anytype,
   2613 ) CompileError {
   2614     const err_msg = try sema.errMsg(src, format, args);
   2615     inline for (args) |arg| {
   2616         if (@TypeOf(arg) == Type.Formatter) {
   2617             try addDeclaredHereNote(sema, err_msg, arg.data.ty);
   2618         }
   2619     }
   2620     return sema.failWithOwnedErrorMsg(block, err_msg);
   2621 }
   2622 
   2623 pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) error{ AnalysisFail, OutOfMemory } {
   2624     @branchHint(.cold);
   2625     const gpa = sema.gpa;
   2626     const zcu = sema.pt.zcu;
   2627 
   2628     if (build_options.enable_debug_extensions and zcu.comp.debug_compile_errors) {
   2629         var wip_errors: std.zig.ErrorBundle.Wip = undefined;
   2630         wip_errors.init(gpa) catch @panic("out of memory");
   2631         Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory");
   2632         std.debug.print("compile error during Sema:\n", .{});
   2633         var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory");
   2634         error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
   2635         crash_report.compilerPanic("unexpected compile error occurred", null);
   2636     }
   2637 
   2638     if (block) |start_block| {
   2639         var block_it = start_block;
   2640         while (block_it.inlining) |inlining| {
   2641             const note_str = note: {
   2642                 if (inlining.is_generic_instantiation) break :note "generic function instantiated here";
   2643                 if (inlining.call_block.isComptime()) break :note "called at comptime here";
   2644                 break :note "called inline here";
   2645             };
   2646             try sema.errNote(inlining.call_src, err_msg, "{s}", .{note_str});
   2647             block_it = inlining.call_block;
   2648         }
   2649     }
   2650 
   2651     err_msg.reference_trace_root = sema.owner.toOptional();
   2652 
   2653     const gop = try zcu.failed_analysis.getOrPut(gpa, sema.owner);
   2654     if (gop.found_existing) {
   2655         // If there are multiple errors for the same Decl, prefer the first one added.
   2656         sema.err = null;
   2657         err_msg.destroy(gpa);
   2658     } else {
   2659         sema.err = err_msg;
   2660         gop.value_ptr.* = err_msg;
   2661     }
   2662 
   2663     return error.AnalysisFail;
   2664 }
   2665 
   2666 /// Given an ErrorMsg, modify its message and source location to the given values, turning the
   2667 /// original message into a note. Notes on the original message are preserved as further notes.
   2668 /// Reference trace is preserved.
   2669 fn reparentOwnedErrorMsg(
   2670     sema: *Sema,
   2671     src: LazySrcLoc,
   2672     msg: *Zcu.ErrorMsg,
   2673     comptime format: []const u8,
   2674     args: anytype,
   2675 ) !void {
   2676     const msg_str = try std.fmt.allocPrint(sema.gpa, format, args);
   2677 
   2678     const orig_notes = msg.notes.len;
   2679     msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1);
   2680     @memmove(msg.notes[1..][0..orig_notes], msg.notes[0..orig_notes]);
   2681     msg.notes[0] = .{
   2682         .src_loc = msg.src_loc,
   2683         .msg = msg.msg,
   2684     };
   2685 
   2686     msg.src_loc = src;
   2687     msg.msg = msg_str;
   2688 }
   2689 
   2690 const align_ty: Type = .u29;
   2691 
   2692 pub fn analyzeAsAlign(
   2693     sema: *Sema,
   2694     block: *Block,
   2695     src: LazySrcLoc,
   2696     air_ref: Air.Inst.Ref,
   2697 ) !Alignment {
   2698     const alignment_big = try sema.analyzeAsInt(
   2699         block,
   2700         src,
   2701         air_ref,
   2702         align_ty,
   2703         .{ .simple = .@"align" },
   2704     );
   2705     return sema.validateAlign(block, src, alignment_big);
   2706 }
   2707 
   2708 fn validateAlign(
   2709     sema: *Sema,
   2710     block: *Block,
   2711     src: LazySrcLoc,
   2712     alignment: u64,
   2713 ) !Alignment {
   2714     if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{});
   2715     if (!std.math.isPowerOfTwo(alignment)) {
   2716         return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{
   2717             alignment,
   2718         });
   2719     }
   2720     return Alignment.fromNonzeroByteUnits(alignment);
   2721 }
   2722 
   2723 fn resolveAlign(
   2724     sema: *Sema,
   2725     block: *Block,
   2726     src: LazySrcLoc,
   2727     zir_ref: Zir.Inst.Ref,
   2728 ) !Alignment {
   2729     const air_ref = try sema.resolveInst(zir_ref);
   2730     return sema.analyzeAsAlign(block, src, air_ref);
   2731 }
   2732 
   2733 fn resolveInt(
   2734     sema: *Sema,
   2735     block: *Block,
   2736     src: LazySrcLoc,
   2737     zir_ref: Zir.Inst.Ref,
   2738     dest_ty: Type,
   2739     reason: ComptimeReason,
   2740 ) !u64 {
   2741     const air_ref = try sema.resolveInst(zir_ref);
   2742     return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason);
   2743 }
   2744 
   2745 fn analyzeAsInt(
   2746     sema: *Sema,
   2747     block: *Block,
   2748     src: LazySrcLoc,
   2749     air_ref: Air.Inst.Ref,
   2750     dest_ty: Type,
   2751     reason: ComptimeReason,
   2752 ) !u64 {
   2753     const coerced = try sema.coerce(block, dest_ty, air_ref, src);
   2754     const val = try sema.resolveConstDefinedValue(block, src, coerced, reason);
   2755     return try val.toUnsignedIntSema(sema.pt);
   2756 }
   2757 
   2758 fn analyzeValueAsCallconv(
   2759     sema: *Sema,
   2760     block: *Block,
   2761     src: LazySrcLoc,
   2762     unresolved_val: Value,
   2763 ) !std.builtin.CallingConvention {
   2764     return interpretBuiltinType(sema, block, src, unresolved_val, std.builtin.CallingConvention);
   2765 }
   2766 
   2767 fn interpretBuiltinType(
   2768     sema: *Sema,
   2769     block: *Block,
   2770     src: LazySrcLoc,
   2771     unresolved_val: Value,
   2772     comptime T: type,
   2773 ) !T {
   2774     const resolved_val = try sema.resolveLazyValue(unresolved_val);
   2775     return resolved_val.interpret(T, sema.pt) catch |err| switch (err) {
   2776         error.OutOfMemory => |e| return e,
   2777         error.UndefinedValue => return sema.failWithUseOfUndef(block, src, null),
   2778         error.TypeMismatch => @panic("std.builtin is corrupt"),
   2779     };
   2780 }
   2781 
   2782 fn zirTupleDecl(
   2783     sema: *Sema,
   2784     block: *Block,
   2785     extended: Zir.Inst.Extended.InstData,
   2786 ) CompileError!Air.Inst.Ref {
   2787     const gpa = sema.gpa;
   2788     const pt = sema.pt;
   2789     const zcu = pt.zcu;
   2790     const fields_len = extended.small;
   2791     const extra = sema.code.extraData(Zir.Inst.TupleDecl, extended.operand);
   2792     var extra_index = extra.end;
   2793 
   2794     const types = try sema.arena.alloc(InternPool.Index, fields_len);
   2795     const inits = try sema.arena.alloc(InternPool.Index, fields_len);
   2796 
   2797     const extra_as_refs: []const Zir.Inst.Ref = @ptrCast(sema.code.extra);
   2798 
   2799     for (types, inits, 0..) |*field_ty, *field_init, field_index| {
   2800         const zir_field_ty, const zir_field_init = extra_as_refs[extra_index..][0..2].*;
   2801         extra_index += 2;
   2802 
   2803         const type_src = block.src(.{ .tuple_field_type = .{
   2804             .tuple_decl_node_offset = extra.data.src_node,
   2805             .elem_index = @intCast(field_index),
   2806         } });
   2807         const init_src = block.src(.{ .tuple_field_init = .{
   2808             .tuple_decl_node_offset = extra.data.src_node,
   2809             .elem_index = @intCast(field_index),
   2810         } });
   2811 
   2812         const field_type = try sema.resolveType(block, type_src, zir_field_ty);
   2813         try sema.validateTupleFieldType(block, field_type, type_src);
   2814 
   2815         field_ty.* = field_type.toIntern();
   2816         field_init.* = init: {
   2817             if (zir_field_init != .none) {
   2818                 const uncoerced_field_init = try sema.resolveInst(zir_field_init);
   2819                 const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src);
   2820                 const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value });
   2821                 if (field_init_val.canMutateComptimeVarState(zcu)) {
   2822                     const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
   2823                     return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val);
   2824                 }
   2825                 break :init field_init_val.toIntern();
   2826             }
   2827             if (try sema.typeHasOnePossibleValue(field_type)) |opv| {
   2828                 break :init opv.toIntern();
   2829             }
   2830             break :init .none;
   2831         };
   2832     }
   2833 
   2834     return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{
   2835         .types = types,
   2836         .values = inits,
   2837     }));
   2838 }
   2839 
   2840 fn validateTupleFieldType(
   2841     sema: *Sema,
   2842     block: *Block,
   2843     field_ty: Type,
   2844     field_ty_src: LazySrcLoc,
   2845 ) CompileError!void {
   2846     const gpa = sema.gpa;
   2847     const zcu = sema.pt.zcu;
   2848     if (field_ty.zigTypeTag(zcu) == .@"opaque") {
   2849         return sema.failWithOwnedErrorMsg(block, msg: {
   2850             const msg = try sema.errMsg(field_ty_src, "opaque types have unknown size and therefore cannot be directly embedded in tuples", .{});
   2851             errdefer msg.destroy(gpa);
   2852 
   2853             try sema.addDeclaredHereNote(msg, field_ty);
   2854             break :msg msg;
   2855         });
   2856     }
   2857     if (field_ty.zigTypeTag(zcu) == .noreturn) {
   2858         return sema.failWithOwnedErrorMsg(block, msg: {
   2859             const msg = try sema.errMsg(field_ty_src, "tuple fields cannot be 'noreturn'", .{});
   2860             errdefer msg.destroy(gpa);
   2861 
   2862             try sema.addDeclaredHereNote(msg, field_ty);
   2863             break :msg msg;
   2864         });
   2865     }
   2866 }
   2867 
   2868 /// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`,
   2869 /// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`.
   2870 fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue {
   2871     const pt = sema.pt;
   2872     const zcu = pt.zcu;
   2873     const ip = &zcu.intern_pool;
   2874     const parent_ty: Type = .fromInterned(zcu.namespacePtr(block.namespace).owner_type);
   2875     const parent_captures: InternPool.CaptureValue.Slice = parent_ty.getCaptures(zcu);
   2876 
   2877     const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len);
   2878 
   2879     for (sema.code.extra[extra_index..][0..captures_len], sema.code.extra[extra_index + captures_len ..][0..captures_len], captures) |raw, raw_name, *capture| {
   2880         const zir_capture: Zir.Inst.Capture = @bitCast(raw);
   2881         const zir_name: Zir.NullTerminatedString = @enumFromInt(raw_name);
   2882         const zir_name_slice = sema.code.nullTerminatedString(zir_name);
   2883         capture.* = switch (zir_capture.unwrap()) {
   2884             .nested => |parent_idx| parent_captures.get(ip)[parent_idx],
   2885             .instruction_load => |ptr_inst| InternPool.CaptureValue.wrap(capture: {
   2886                 const ptr_ref = try sema.resolveInst(ptr_inst.toRef());
   2887                 const ptr_val = try sema.resolveValue(ptr_ref) orelse {
   2888                     break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() };
   2889                 };
   2890                 // TODO: better source location
   2891                 const unresolved_loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse {
   2892                     break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() };
   2893                 };
   2894                 const loaded_val = try sema.resolveLazyValue(unresolved_loaded_val);
   2895                 if (loaded_val.canMutateComptimeVarState(zcu)) {
   2896                     const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls);
   2897                     return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", loaded_val);
   2898                 }
   2899                 break :capture .{ .@"comptime" = loaded_val.toIntern() };
   2900             }),
   2901             .instruction => |inst| InternPool.CaptureValue.wrap(capture: {
   2902                 const air_ref = try sema.resolveInst(inst.toRef());
   2903                 if (try sema.resolveValueResolveLazy(air_ref)) |val| {
   2904                     if (val.canMutateComptimeVarState(zcu)) {
   2905                         const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls);
   2906                         return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", val);
   2907                     }
   2908                     break :capture .{ .@"comptime" = val.toIntern() };
   2909                 }
   2910                 break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() };
   2911             }),
   2912             .decl_val => |str| capture: {
   2913                 const decl_name = try ip.getOrPutString(
   2914                     sema.gpa,
   2915                     pt.tid,
   2916                     sema.code.nullTerminatedString(str),
   2917                     .no_embedded_nulls,
   2918                 );
   2919                 const nav = try sema.lookupIdentifier(block, decl_name);
   2920                 break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav });
   2921             },
   2922             .decl_ref => |str| capture: {
   2923                 const decl_name = try ip.getOrPutString(
   2924                     sema.gpa,
   2925                     pt.tid,
   2926                     sema.code.nullTerminatedString(str),
   2927                     .no_embedded_nulls,
   2928                 );
   2929                 const nav = try sema.lookupIdentifier(block, decl_name);
   2930                 break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav });
   2931             },
   2932         };
   2933     }
   2934 
   2935     return captures;
   2936 }
   2937 
   2938 fn zirStructDecl(
   2939     sema: *Sema,
   2940     block: *Block,
   2941     extended: Zir.Inst.Extended.InstData,
   2942     inst: Zir.Inst.Index,
   2943 ) CompileError!Air.Inst.Ref {
   2944     const pt = sema.pt;
   2945     const zcu = pt.zcu;
   2946     const gpa = sema.gpa;
   2947     const ip = &zcu.intern_pool;
   2948     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
   2949     const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand);
   2950 
   2951     const tracked_inst = try block.trackZir(inst);
   2952     const src: LazySrcLoc = .{
   2953         .base_node_inst = tracked_inst,
   2954         .offset = LazySrcLoc.Offset.nodeOffset(.zero),
   2955     };
   2956 
   2957     var extra_index = extra.end;
   2958 
   2959     const captures_len = if (small.has_captures_len) blk: {
   2960         const captures_len = sema.code.extra[extra_index];
   2961         extra_index += 1;
   2962         break :blk captures_len;
   2963     } else 0;
   2964     const fields_len = if (small.has_fields_len) blk: {
   2965         const fields_len = sema.code.extra[extra_index];
   2966         extra_index += 1;
   2967         break :blk fields_len;
   2968     } else 0;
   2969     const decls_len = if (small.has_decls_len) blk: {
   2970         const decls_len = sema.code.extra[extra_index];
   2971         extra_index += 1;
   2972         break :blk decls_len;
   2973     } else 0;
   2974 
   2975     const captures = try sema.getCaptures(block, src, extra_index, captures_len);
   2976     extra_index += captures_len * 2;
   2977 
   2978     if (small.has_backing_int) {
   2979         const backing_int_body_len = sema.code.extra[extra_index];
   2980         extra_index += 1; // backing_int_body_len
   2981         if (backing_int_body_len == 0) {
   2982             extra_index += 1; // backing_int_ref
   2983         } else {
   2984             extra_index += backing_int_body_len; // backing_int_body_inst
   2985         }
   2986     }
   2987 
   2988     const struct_init: InternPool.StructTypeInit = .{
   2989         .layout = small.layout,
   2990         .fields_len = fields_len,
   2991         .known_non_opv = small.known_non_opv,
   2992         .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
   2993         .any_comptime_fields = small.any_comptime_fields,
   2994         .any_default_inits = small.any_default_inits,
   2995         .inits_resolved = false,
   2996         .any_aligned_fields = small.any_aligned_fields,
   2997         .key = .{ .declared = .{
   2998             .zir_index = tracked_inst,
   2999             .captures = captures,
   3000         } },
   3001     };
   3002     const wip_ty = switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) {
   3003         .existing => |ty| {
   3004             const new_ty = try pt.ensureTypeUpToDate(ty);
   3005 
   3006             // Make sure we update the namespace if the declaration is re-analyzed, to pick
   3007             // up on e.g. changed comptime decls.
   3008             try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu));
   3009 
   3010             try sema.declareDependency(.{ .interned = new_ty });
   3011             try sema.addTypeReferenceEntry(src, new_ty);
   3012             return Air.internedToRef(new_ty);
   3013         },
   3014         .wip => |wip| wip,
   3015     };
   3016     errdefer wip_ty.cancel(ip, pt.tid);
   3017 
   3018     const type_name = try sema.createTypeName(
   3019         block,
   3020         small.name_strategy,
   3021         "struct",
   3022         inst,
   3023         wip_ty.index,
   3024     );
   3025     wip_ty.setName(ip, type_name.name, type_name.nav);
   3026 
   3027     const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
   3028         .parent = block.namespace.toOptional(),
   3029         .owner_type = wip_ty.index,
   3030         .file_scope = block.getFileScopeIndex(zcu),
   3031         .generation = zcu.generation,
   3032     });
   3033     errdefer pt.destroyNamespace(new_namespace_index);
   3034 
   3035     if (pt.zcu.comp.incremental) {
   3036         try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
   3037     }
   3038 
   3039     const decls = sema.code.bodySlice(extra_index, decls_len);
   3040     try pt.scanNamespace(new_namespace_index, decls);
   3041 
   3042     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
   3043     codegen_type: {
   3044         if (zcu.comp.config.use_llvm) break :codegen_type;
   3045         if (block.ownerModule().strip) break :codegen_type;
   3046         // This job depends on any resolve_type_fully jobs queued up before it.
   3047         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   3048         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   3049     }
   3050     try sema.declareDependency(.{ .interned = wip_ty.index });
   3051     try sema.addTypeReferenceEntry(src, wip_ty.index);
   3052     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   3053     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
   3054 }
   3055 
   3056 pub fn createTypeName(
   3057     sema: *Sema,
   3058     block: *Block,
   3059     name_strategy: Zir.Inst.NameStrategy,
   3060     anon_prefix: []const u8,
   3061     inst: ?Zir.Inst.Index,
   3062     /// This is used purely to give the type a unique name in the `anon` case.
   3063     type_index: InternPool.Index,
   3064 ) CompileError!struct {
   3065     name: InternPool.NullTerminatedString,
   3066     nav: InternPool.Nav.Index.Optional,
   3067 } {
   3068     const pt = sema.pt;
   3069     const zcu = pt.zcu;
   3070     const gpa = zcu.gpa;
   3071     const ip = &zcu.intern_pool;
   3072 
   3073     switch (name_strategy) {
   3074         .anon => {}, // handled after switch
   3075         .parent => return .{
   3076             .name = block.type_name_ctx,
   3077             .nav = sema.owner.unwrap().nav_val.toOptional(),
   3078         },
   3079         .func => func_strat: {
   3080             const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail);
   3081             const zir_tags = sema.code.instructions.items(.tag);
   3082 
   3083             var aw: std.Io.Writer.Allocating = .init(gpa);
   3084             defer aw.deinit();
   3085             const w = &aw.writer;
   3086             w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory;
   3087 
   3088             var arg_i: usize = 0;
   3089             for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) {
   3090                 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
   3091                     const arg = sema.inst_map.get(zir_inst).?;
   3092                     // If this is being called in a generic function then analyzeCall will
   3093                     // have already resolved the args and this will work.
   3094                     // If not then this is a struct type being returned from a non-generic
   3095                     // function and the name doesn't matter since it will later
   3096                     // result in a compile error.
   3097                     const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat
   3098 
   3099                     if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory;
   3100 
   3101                     // Limiting the depth here helps avoid type names getting too long, which
   3102                     // in turn helps to avoid unreasonably long symbol names for namespaced
   3103                     // symbols. Such names should ideally be human-readable, and additionally,
   3104                     // some tooling may not support very long symbol names.
   3105                     w.print("{f}", .{Value.fmtValueSemaFull(.{
   3106                         .val = arg_val,
   3107                         .pt = pt,
   3108                         .opt_sema = sema,
   3109                         .depth = 1,
   3110                     })}) catch return error.OutOfMemory;
   3111 
   3112                     arg_i += 1;
   3113                     continue;
   3114                 },
   3115                 else => continue,
   3116             };
   3117 
   3118             w.writeByte(')') catch return error.OutOfMemory;
   3119             return .{
   3120                 .name = try ip.getOrPutString(gpa, pt.tid, aw.written(), .no_embedded_nulls),
   3121                 .nav = .none,
   3122             };
   3123         },
   3124         .dbg_var => {
   3125             // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions.
   3126             const ref = inst.?.toRef();
   3127             const zir_tags = sema.code.instructions.items(.tag);
   3128             const zir_data = sema.code.instructions.items(.data);
   3129             for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) {
   3130                 .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) {
   3131                     return .{
   3132                         .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}.{s}", .{
   3133                             block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code),
   3134                         }, .no_embedded_nulls),
   3135                         .nav = .none,
   3136                     };
   3137                 },
   3138                 else => {},
   3139             };
   3140             // fall through to anon strat
   3141         },
   3142     }
   3143 
   3144     // anon strat handling
   3145 
   3146     // It would be neat to have "struct:line:column" but this name has
   3147     // to survive incremental updates, where it may have been shifted down
   3148     // or up to a different line, but unchanged, and thus not unnecessarily
   3149     // semantically analyzed.
   3150     // TODO: that would be possible, by detecting line number changes and renaming
   3151     // types appropriately. However, `@typeName` becomes a problem then. If we remove
   3152     // that builtin from the language, we can consider this.
   3153 
   3154     return .{
   3155         .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}__{s}_{d}", .{
   3156             block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(type_index),
   3157         }, .no_embedded_nulls),
   3158         .nav = .none,
   3159     };
   3160 }
   3161 
   3162 fn zirEnumDecl(
   3163     sema: *Sema,
   3164     block: *Block,
   3165     extended: Zir.Inst.Extended.InstData,
   3166     inst: Zir.Inst.Index,
   3167 ) CompileError!Air.Inst.Ref {
   3168     const tracy = trace(@src());
   3169     defer tracy.end();
   3170 
   3171     const pt = sema.pt;
   3172     const zcu = pt.zcu;
   3173     const gpa = sema.gpa;
   3174     const ip = &zcu.intern_pool;
   3175     const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
   3176     const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand);
   3177     var extra_index: usize = extra.end;
   3178 
   3179     const tracked_inst = try block.trackZir(inst);
   3180     const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
   3181 
   3182     const tag_type_ref = if (small.has_tag_type) blk: {
   3183         const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   3184         extra_index += 1;
   3185         break :blk tag_type_ref;
   3186     } else .none;
   3187 
   3188     const captures_len = if (small.has_captures_len) blk: {
   3189         const captures_len = sema.code.extra[extra_index];
   3190         extra_index += 1;
   3191         break :blk captures_len;
   3192     } else 0;
   3193 
   3194     const body_len = if (small.has_body_len) blk: {
   3195         const body_len = sema.code.extra[extra_index];
   3196         extra_index += 1;
   3197         break :blk body_len;
   3198     } else 0;
   3199 
   3200     const fields_len = if (small.has_fields_len) blk: {
   3201         const fields_len = sema.code.extra[extra_index];
   3202         extra_index += 1;
   3203         break :blk fields_len;
   3204     } else 0;
   3205 
   3206     const decls_len = if (small.has_decls_len) blk: {
   3207         const decls_len = sema.code.extra[extra_index];
   3208         extra_index += 1;
   3209         break :blk decls_len;
   3210     } else 0;
   3211 
   3212     const captures = try sema.getCaptures(block, src, extra_index, captures_len);
   3213     extra_index += captures_len * 2;
   3214 
   3215     const decls = sema.code.bodySlice(extra_index, decls_len);
   3216     extra_index += decls_len;
   3217 
   3218     const body = sema.code.bodySlice(extra_index, body_len);
   3219     extra_index += body.len;
   3220 
   3221     const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
   3222     const body_end = extra_index;
   3223     extra_index += bit_bags_count;
   3224 
   3225     const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| {
   3226         if (bag != 0) break true;
   3227     } else false;
   3228 
   3229     const enum_init: InternPool.EnumTypeInit = .{
   3230         .has_values = any_values,
   3231         .tag_mode = if (small.nonexhaustive)
   3232             .nonexhaustive
   3233         else if (tag_type_ref == .none)
   3234             .auto
   3235         else
   3236             .explicit,
   3237         .fields_len = fields_len,
   3238         .key = .{ .declared = .{
   3239             .zir_index = tracked_inst,
   3240             .captures = captures,
   3241         } },
   3242     };
   3243     const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) {
   3244         .existing => |ty| {
   3245             const new_ty = try pt.ensureTypeUpToDate(ty);
   3246 
   3247             // Make sure we update the namespace if the declaration is re-analyzed, to pick
   3248             // up on e.g. changed comptime decls.
   3249             try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu));
   3250 
   3251             try sema.declareDependency(.{ .interned = new_ty });
   3252             try sema.addTypeReferenceEntry(src, new_ty);
   3253 
   3254             // Since this is an enum, it has to be resolved immediately.
   3255             // `ensureTypeUpToDate` has resolved the new type if necessary.
   3256             // We just need to check for resolution failures.
   3257             const ty_unit: AnalUnit = .wrap(.{ .type = new_ty });
   3258             if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) {
   3259                 return error.AnalysisFail;
   3260             }
   3261 
   3262             return Air.internedToRef(new_ty);
   3263         },
   3264         .wip => |wip| wip,
   3265     };
   3266 
   3267     // Once this is `true`, we will not delete the decl or type even upon failure, since we
   3268     // have finished constructing the type and are in the process of analyzing it.
   3269     var done = false;
   3270 
   3271     errdefer if (!done) wip_ty.cancel(ip, pt.tid);
   3272 
   3273     const type_name = try sema.createTypeName(
   3274         block,
   3275         small.name_strategy,
   3276         "enum",
   3277         inst,
   3278         wip_ty.index,
   3279     );
   3280     wip_ty.setName(ip, type_name.name, type_name.nav);
   3281 
   3282     const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
   3283         .parent = block.namespace.toOptional(),
   3284         .owner_type = wip_ty.index,
   3285         .file_scope = block.getFileScopeIndex(zcu),
   3286         .generation = zcu.generation,
   3287     });
   3288     errdefer if (!done) pt.destroyNamespace(new_namespace_index);
   3289 
   3290     try pt.scanNamespace(new_namespace_index, decls);
   3291 
   3292     try sema.declareDependency(.{ .interned = wip_ty.index });
   3293     try sema.addTypeReferenceEntry(src, wip_ty.index);
   3294 
   3295     // We've finished the initial construction of this type, and are about to perform analysis.
   3296     // Set the namespace appropriately, and don't destroy anything on failure.
   3297     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   3298     wip_ty.prepare(ip, new_namespace_index);
   3299     done = true;
   3300 
   3301     {
   3302         const tracked_unit = zcu.trackUnitSema(type_name.name.toSlice(ip), null);
   3303         defer tracked_unit.end(zcu);
   3304         try Sema.resolveDeclaredEnum(
   3305             pt,
   3306             wip_ty,
   3307             inst,
   3308             tracked_inst,
   3309             new_namespace_index,
   3310             type_name.name,
   3311             small,
   3312             body,
   3313             tag_type_ref,
   3314             any_values,
   3315             fields_len,
   3316             sema.code,
   3317             body_end,
   3318         );
   3319     }
   3320 
   3321     codegen_type: {
   3322         if (zcu.comp.config.use_llvm) break :codegen_type;
   3323         if (block.ownerModule().strip) break :codegen_type;
   3324         // This job depends on any resolve_type_fully jobs queued up before it.
   3325         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   3326         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   3327     }
   3328     return Air.internedToRef(wip_ty.index);
   3329 }
   3330 
   3331 fn zirUnionDecl(
   3332     sema: *Sema,
   3333     block: *Block,
   3334     extended: Zir.Inst.Extended.InstData,
   3335     inst: Zir.Inst.Index,
   3336 ) CompileError!Air.Inst.Ref {
   3337     const tracy = trace(@src());
   3338     defer tracy.end();
   3339 
   3340     const pt = sema.pt;
   3341     const zcu = pt.zcu;
   3342     const gpa = sema.gpa;
   3343     const ip = &zcu.intern_pool;
   3344     const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
   3345     const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand);
   3346     var extra_index: usize = extra.end;
   3347 
   3348     const tracked_inst = try block.trackZir(inst);
   3349     const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
   3350 
   3351     extra_index += @intFromBool(small.has_tag_type);
   3352     const captures_len = if (small.has_captures_len) blk: {
   3353         const captures_len = sema.code.extra[extra_index];
   3354         extra_index += 1;
   3355         break :blk captures_len;
   3356     } else 0;
   3357     extra_index += @intFromBool(small.has_body_len);
   3358     const fields_len = if (small.has_fields_len) blk: {
   3359         const fields_len = sema.code.extra[extra_index];
   3360         extra_index += 1;
   3361         break :blk fields_len;
   3362     } else 0;
   3363 
   3364     const decls_len = if (small.has_decls_len) blk: {
   3365         const decls_len = sema.code.extra[extra_index];
   3366         extra_index += 1;
   3367         break :blk decls_len;
   3368     } else 0;
   3369 
   3370     const captures = try sema.getCaptures(block, src, extra_index, captures_len);
   3371     extra_index += captures_len * 2;
   3372 
   3373     const union_init: InternPool.UnionTypeInit = .{
   3374         .flags = .{
   3375             .layout = small.layout,
   3376             .status = .none,
   3377             .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
   3378                 .tagged
   3379             else if (small.layout != .auto)
   3380                 .none
   3381             else switch (block.wantSafeTypes()) {
   3382                 true => .safety,
   3383                 false => .none,
   3384             },
   3385             .any_aligned_fields = small.any_aligned_fields,
   3386             .requires_comptime = .unknown,
   3387             .assumed_runtime_bits = false,
   3388             .assumed_pointer_aligned = false,
   3389             .alignment = .none,
   3390         },
   3391         .fields_len = fields_len,
   3392         .enum_tag_ty = .none, // set later
   3393         .field_types = &.{}, // set later
   3394         .field_aligns = &.{}, // set later
   3395         .key = .{ .declared = .{
   3396             .zir_index = tracked_inst,
   3397             .captures = captures,
   3398         } },
   3399     };
   3400     const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) {
   3401         .existing => |ty| {
   3402             const new_ty = try pt.ensureTypeUpToDate(ty);
   3403 
   3404             // Make sure we update the namespace if the declaration is re-analyzed, to pick
   3405             // up on e.g. changed comptime decls.
   3406             try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu));
   3407 
   3408             try sema.declareDependency(.{ .interned = new_ty });
   3409             try sema.addTypeReferenceEntry(src, new_ty);
   3410             return Air.internedToRef(new_ty);
   3411         },
   3412         .wip => |wip| wip,
   3413     };
   3414     errdefer wip_ty.cancel(ip, pt.tid);
   3415 
   3416     const type_name = try sema.createTypeName(
   3417         block,
   3418         small.name_strategy,
   3419         "union",
   3420         inst,
   3421         wip_ty.index,
   3422     );
   3423     wip_ty.setName(ip, type_name.name, type_name.nav);
   3424 
   3425     const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
   3426         .parent = block.namespace.toOptional(),
   3427         .owner_type = wip_ty.index,
   3428         .file_scope = block.getFileScopeIndex(zcu),
   3429         .generation = zcu.generation,
   3430     });
   3431     errdefer pt.destroyNamespace(new_namespace_index);
   3432 
   3433     if (pt.zcu.comp.incremental) {
   3434         try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
   3435     }
   3436 
   3437     const decls = sema.code.bodySlice(extra_index, decls_len);
   3438     try pt.scanNamespace(new_namespace_index, decls);
   3439 
   3440     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
   3441     codegen_type: {
   3442         if (zcu.comp.config.use_llvm) break :codegen_type;
   3443         if (block.ownerModule().strip) break :codegen_type;
   3444         // This job depends on any resolve_type_fully jobs queued up before it.
   3445         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   3446         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   3447     }
   3448     try sema.declareDependency(.{ .interned = wip_ty.index });
   3449     try sema.addTypeReferenceEntry(src, wip_ty.index);
   3450     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   3451     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
   3452 }
   3453 
   3454 fn zirOpaqueDecl(
   3455     sema: *Sema,
   3456     block: *Block,
   3457     extended: Zir.Inst.Extended.InstData,
   3458     inst: Zir.Inst.Index,
   3459 ) CompileError!Air.Inst.Ref {
   3460     const tracy = trace(@src());
   3461     defer tracy.end();
   3462 
   3463     const pt = sema.pt;
   3464     const zcu = pt.zcu;
   3465     const gpa = sema.gpa;
   3466     const ip = &zcu.intern_pool;
   3467 
   3468     const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
   3469     const extra = sema.code.extraData(Zir.Inst.OpaqueDecl, extended.operand);
   3470     var extra_index: usize = extra.end;
   3471 
   3472     const tracked_inst = try block.trackZir(inst);
   3473     const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
   3474 
   3475     const captures_len = if (small.has_captures_len) blk: {
   3476         const captures_len = sema.code.extra[extra_index];
   3477         extra_index += 1;
   3478         break :blk captures_len;
   3479     } else 0;
   3480 
   3481     const decls_len = if (small.has_decls_len) blk: {
   3482         const decls_len = sema.code.extra[extra_index];
   3483         extra_index += 1;
   3484         break :blk decls_len;
   3485     } else 0;
   3486 
   3487     const captures = try sema.getCaptures(block, src, extra_index, captures_len);
   3488     extra_index += captures_len * 2;
   3489 
   3490     const opaque_init: InternPool.OpaqueTypeInit = .{
   3491         .key = .{ .declared = .{
   3492             .zir_index = tracked_inst,
   3493             .captures = captures,
   3494         } },
   3495     };
   3496     const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) {
   3497         .existing => |ty| {
   3498             // Make sure we update the namespace if the declaration is re-analyzed, to pick
   3499             // up on e.g. changed comptime decls.
   3500             try pt.ensureNamespaceUpToDate(Type.fromInterned(ty).getNamespaceIndex(zcu));
   3501 
   3502             try sema.declareDependency(.{ .interned = ty });
   3503             try sema.addTypeReferenceEntry(src, ty);
   3504             return Air.internedToRef(ty);
   3505         },
   3506         .wip => |wip| wip,
   3507     };
   3508     errdefer wip_ty.cancel(ip, pt.tid);
   3509 
   3510     const type_name = try sema.createTypeName(
   3511         block,
   3512         small.name_strategy,
   3513         "opaque",
   3514         inst,
   3515         wip_ty.index,
   3516     );
   3517     wip_ty.setName(ip, type_name.name, type_name.nav);
   3518 
   3519     const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
   3520         .parent = block.namespace.toOptional(),
   3521         .owner_type = wip_ty.index,
   3522         .file_scope = block.getFileScopeIndex(zcu),
   3523         .generation = zcu.generation,
   3524     });
   3525     errdefer pt.destroyNamespace(new_namespace_index);
   3526 
   3527     const decls = sema.code.bodySlice(extra_index, decls_len);
   3528     try pt.scanNamespace(new_namespace_index, decls);
   3529 
   3530     codegen_type: {
   3531         if (zcu.comp.config.use_llvm) break :codegen_type;
   3532         if (block.ownerModule().strip) break :codegen_type;
   3533         // This job depends on any resolve_type_fully jobs queued up before it.
   3534         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   3535         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   3536     }
   3537     try sema.addTypeReferenceEntry(src, wip_ty.index);
   3538     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   3539     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
   3540 }
   3541 
   3542 fn zirErrorSetDecl(
   3543     sema: *Sema,
   3544     inst: Zir.Inst.Index,
   3545 ) CompileError!Air.Inst.Ref {
   3546     const tracy = trace(@src());
   3547     defer tracy.end();
   3548 
   3549     const pt = sema.pt;
   3550     const zcu = pt.zcu;
   3551     const gpa = sema.gpa;
   3552     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   3553     const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index);
   3554 
   3555     var names: InferredErrorSet.NameMap = .{};
   3556     try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len);
   3557 
   3558     var extra_index: u32 = @intCast(extra.end);
   3559     const extra_index_end = extra_index + extra.data.fields_len;
   3560     while (extra_index < extra_index_end) : (extra_index += 1) {
   3561         const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]);
   3562         const name = sema.code.nullTerminatedString(name_index);
   3563         const name_ip = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
   3564         _ = try pt.getErrorValue(name_ip);
   3565         const result = names.getOrPutAssumeCapacity(name_ip);
   3566         assert(!result.found_existing); // verified in AstGen
   3567     }
   3568 
   3569     return Air.internedToRef((try pt.errorSetFromUnsortedNames(names.keys())).toIntern());
   3570 }
   3571 
   3572 fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3573     const tracy = trace(@src());
   3574     defer tracy.end();
   3575 
   3576     const pt = sema.pt;
   3577 
   3578     const src = block.nodeOffset(sema.code.instructions.items(.data)[@intFromEnum(inst)].node);
   3579 
   3580     if (block.isComptime() or try sema.fn_ret_ty.comptimeOnlySema(pt)) {
   3581         try sema.fn_ret_ty.resolveFields(pt);
   3582         return sema.analyzeComptimeAlloc(block, src, sema.fn_ret_ty, .none);
   3583     }
   3584 
   3585     const target = pt.zcu.getTarget();
   3586     const ptr_type = try pt.ptrTypeSema(.{
   3587         .child = sema.fn_ret_ty.toIntern(),
   3588         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   3589     });
   3590 
   3591     if (block.inlining != null) {
   3592         // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr.
   3593         // TODO when functions gain result location support, the inlining struct in
   3594         // Block should contain the return pointer, and we would pass that through here.
   3595         return block.addTy(.alloc, ptr_type);
   3596     }
   3597 
   3598     return block.addTy(.ret_ptr, ptr_type);
   3599 }
   3600 
   3601 fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3602     const tracy = trace(@src());
   3603     defer tracy.end();
   3604 
   3605     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
   3606     const operand = try sema.resolveInst(inst_data.operand);
   3607     return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand);
   3608 }
   3609 
   3610 fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3611     const tracy = trace(@src());
   3612     defer tracy.end();
   3613 
   3614     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3615     const operand = try sema.resolveInst(inst_data.operand);
   3616     const src = block.nodeOffset(inst_data.src_node);
   3617 
   3618     return sema.ensureResultUsed(block, sema.typeOf(operand), src);
   3619 }
   3620 
   3621 fn ensureResultUsed(
   3622     sema: *Sema,
   3623     block: *Block,
   3624     ty: Type,
   3625     src: LazySrcLoc,
   3626 ) CompileError!void {
   3627     const pt = sema.pt;
   3628     const zcu = pt.zcu;
   3629     switch (ty.zigTypeTag(zcu)) {
   3630         .void, .noreturn => return,
   3631         .error_set => return sema.fail(block, src, "error set is ignored", .{}),
   3632         .error_union => {
   3633             const msg = msg: {
   3634                 const msg = try sema.errMsg(src, "error union is ignored", .{});
   3635                 errdefer msg.destroy(sema.gpa);
   3636                 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   3637                 break :msg msg;
   3638             };
   3639             return sema.failWithOwnedErrorMsg(block, msg);
   3640         },
   3641         else => {
   3642             const msg = msg: {
   3643                 const msg = try sema.errMsg(src, "value of type '{f}' ignored", .{ty.fmt(pt)});
   3644                 errdefer msg.destroy(sema.gpa);
   3645                 try sema.errNote(src, msg, "all non-void values must be used", .{});
   3646                 try sema.errNote(src, msg, "to discard the value, assign it to '_'", .{});
   3647                 break :msg msg;
   3648             };
   3649             return sema.failWithOwnedErrorMsg(block, msg);
   3650         },
   3651     }
   3652 }
   3653 
   3654 fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3655     const tracy = trace(@src());
   3656     defer tracy.end();
   3657 
   3658     const pt = sema.pt;
   3659     const zcu = pt.zcu;
   3660     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3661     const operand = try sema.resolveInst(inst_data.operand);
   3662     const src = block.nodeOffset(inst_data.src_node);
   3663     const operand_ty = sema.typeOf(operand);
   3664     switch (operand_ty.zigTypeTag(zcu)) {
   3665         .error_set => return sema.fail(block, src, "error set is discarded", .{}),
   3666         .error_union => {
   3667             const msg = msg: {
   3668                 const msg = try sema.errMsg(src, "error union is discarded", .{});
   3669                 errdefer msg.destroy(sema.gpa);
   3670                 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   3671                 break :msg msg;
   3672             };
   3673             return sema.failWithOwnedErrorMsg(block, msg);
   3674         },
   3675         else => return,
   3676     }
   3677 }
   3678 
   3679 fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3680     const tracy = trace(@src());
   3681     defer tracy.end();
   3682 
   3683     const pt = sema.pt;
   3684     const zcu = pt.zcu;
   3685     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3686     const src = block.nodeOffset(inst_data.src_node);
   3687     const operand = try sema.resolveInst(inst_data.operand);
   3688     const operand_ty = sema.typeOf(operand);
   3689     const err_union_ty = if (operand_ty.zigTypeTag(zcu) == .pointer)
   3690         operand_ty.childType(zcu)
   3691     else
   3692         operand_ty;
   3693     if (err_union_ty.zigTypeTag(zcu) != .error_union) return;
   3694     const payload_ty = err_union_ty.errorUnionPayload(zcu).zigTypeTag(zcu);
   3695     if (payload_ty != .void and payload_ty != .noreturn) {
   3696         const msg = msg: {
   3697             const msg = try sema.errMsg(src, "error union payload is ignored", .{});
   3698             errdefer msg.destroy(sema.gpa);
   3699             try sema.errNote(src, msg, "payload value can be explicitly ignored with '|_|'", .{});
   3700             break :msg msg;
   3701         };
   3702         return sema.failWithOwnedErrorMsg(block, msg);
   3703     }
   3704 }
   3705 
   3706 fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3707     const tracy = trace(@src());
   3708     defer tracy.end();
   3709 
   3710     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3711     const src = block.nodeOffset(inst_data.src_node);
   3712     const object = try sema.resolveInst(inst_data.operand);
   3713 
   3714     return indexablePtrLen(sema, block, src, object);
   3715 }
   3716 
   3717 fn indexablePtrLen(
   3718     sema: *Sema,
   3719     block: *Block,
   3720     src: LazySrcLoc,
   3721     object: Air.Inst.Ref,
   3722 ) CompileError!Air.Inst.Ref {
   3723     const pt = sema.pt;
   3724     const zcu = pt.zcu;
   3725     const object_ty = sema.typeOf(object);
   3726     const is_pointer_to = object_ty.isSinglePointer(zcu);
   3727     const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty;
   3728     try sema.checkIndexable(block, src, indexable_ty);
   3729     const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls);
   3730     return sema.fieldVal(block, src, object, field_name, src);
   3731 }
   3732 
   3733 fn indexablePtrLenOrNone(
   3734     sema: *Sema,
   3735     block: *Block,
   3736     src: LazySrcLoc,
   3737     operand: Air.Inst.Ref,
   3738 ) CompileError!Air.Inst.Ref {
   3739     const pt = sema.pt;
   3740     const zcu = pt.zcu;
   3741     const operand_ty = sema.typeOf(operand);
   3742     try checkMemOperand(sema, block, src, operand_ty);
   3743     switch (operand_ty.ptrSize(zcu)) {
   3744         .many, .c => return .none,
   3745         .one, .slice => {},
   3746     }
   3747     const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls);
   3748     return sema.fieldVal(block, src, operand, field_name, src);
   3749 }
   3750 
   3751 fn zirAllocExtended(
   3752     sema: *Sema,
   3753     block: *Block,
   3754     extended: Zir.Inst.Extended.InstData,
   3755 ) CompileError!Air.Inst.Ref {
   3756     const pt = sema.pt;
   3757     const gpa = sema.gpa;
   3758     const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
   3759     const var_src = block.nodeOffset(extra.data.src_node);
   3760     const ty_src = block.src(.{ .node_offset_var_decl_ty = extra.data.src_node });
   3761     const align_src = block.src(.{ .node_offset_var_decl_align = extra.data.src_node });
   3762     const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small);
   3763 
   3764     var extra_index: usize = extra.end;
   3765 
   3766     const var_ty: Type = if (small.has_type) blk: {
   3767         const type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   3768         extra_index += 1;
   3769         break :blk try sema.resolveType(block, ty_src, type_ref);
   3770     } else undefined;
   3771 
   3772     const alignment = if (small.has_align) blk: {
   3773         const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   3774         extra_index += 1;
   3775         break :blk try sema.resolveAlign(block, align_src, align_ref);
   3776     } else .none;
   3777 
   3778     if (block.isComptime() or small.is_comptime) {
   3779         if (small.has_type) {
   3780             return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment);
   3781         } else {
   3782             try sema.air_instructions.append(gpa, .{
   3783                 .tag = .inferred_alloc_comptime,
   3784                 .data = .{ .inferred_alloc_comptime = .{
   3785                     .alignment = alignment,
   3786                     .is_const = small.is_const,
   3787                     .ptr = undefined,
   3788                 } },
   3789             });
   3790             return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
   3791         }
   3792     }
   3793 
   3794     if (small.has_type and try var_ty.comptimeOnlySema(pt)) {
   3795         return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment);
   3796     }
   3797 
   3798     if (small.has_type) {
   3799         if (!small.is_const) {
   3800             try sema.validateVarType(block, ty_src, var_ty, false);
   3801         }
   3802         const target = pt.zcu.getTarget();
   3803         try var_ty.resolveLayout(pt);
   3804         if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
   3805             const store_src = block.src(.{ .node_offset_store_ptr = extra.data.src_node });
   3806             return sema.fail(block, store_src, "local variable in naked function", .{});
   3807         }
   3808         const ptr_type = try sema.pt.ptrTypeSema(.{
   3809             .child = var_ty.toIntern(),
   3810             .flags = .{
   3811                 .alignment = alignment,
   3812                 .address_space = target_util.defaultAddressSpace(target, .local),
   3813             },
   3814         });
   3815         const ptr = try block.addTy(.alloc, ptr_type);
   3816         if (small.is_const) {
   3817             const ptr_inst = ptr.toIndex().?;
   3818             try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index });
   3819             try sema.base_allocs.put(gpa, ptr_inst, ptr_inst);
   3820         }
   3821         return ptr;
   3822     }
   3823 
   3824     const result_index = try block.addInstAsIndex(.{
   3825         .tag = .inferred_alloc,
   3826         .data = .{ .inferred_alloc = .{
   3827             .alignment = alignment,
   3828             .is_const = small.is_const,
   3829         } },
   3830     });
   3831     try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
   3832     if (small.is_const) {
   3833         try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index });
   3834         try sema.base_allocs.put(gpa, result_index, result_index);
   3835     }
   3836     return result_index.toRef();
   3837 }
   3838 
   3839 fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3840     const tracy = trace(@src());
   3841     defer tracy.end();
   3842 
   3843     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3844     const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
   3845     const var_src = block.nodeOffset(inst_data.src_node);
   3846     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   3847     return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
   3848 }
   3849 
   3850 fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3851     const pt = sema.pt;
   3852     const zcu = pt.zcu;
   3853     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3854     const alloc = try sema.resolveInst(inst_data.operand);
   3855     const alloc_ty = sema.typeOf(alloc);
   3856     const ptr_info = alloc_ty.ptrInfo(zcu);
   3857     const elem_ty: Type = .fromInterned(ptr_info.child);
   3858 
   3859     // If the alloc was created in a comptime scope, we already created a comptime alloc for it.
   3860     // However, if the final constructed value does not reference comptime-mutable memory, we wish
   3861     // to promote it to an anon decl.
   3862     already_ct: {
   3863         const ptr_val = try sema.resolveValue(alloc) orelse break :already_ct;
   3864 
   3865         // If this was a comptime inferred alloc, then `storeToInferredAllocComptime`
   3866         // might have already done our job and created an anon decl ref.
   3867         switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
   3868             .ptr => |ptr| switch (ptr.base_addr) {
   3869                 .uav => {
   3870                     // The comptime-ification was already done for us.
   3871                     // Just make sure the pointer is const.
   3872                     return sema.makePtrConst(block, alloc);
   3873                 },
   3874                 else => {},
   3875             },
   3876             else => {},
   3877         }
   3878 
   3879         if (!sema.isComptimeMutablePtr(ptr_val)) break :already_ct;
   3880         const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
   3881         assert(ptr.byte_offset == 0);
   3882         const alloc_index = ptr.base_addr.comptime_alloc;
   3883         const ct_alloc = sema.getComptimeAlloc(alloc_index);
   3884         const interned = try ct_alloc.val.intern(pt, sema.arena);
   3885         if (interned.canMutateComptimeVarState(zcu)) {
   3886             // Preserve the comptime alloc, just make the pointer const.
   3887             ct_alloc.val = .{ .interned = interned.toIntern() };
   3888             ct_alloc.is_const = true;
   3889             return sema.makePtrConst(block, alloc);
   3890         } else {
   3891             // Promote the constant to an anon decl.
   3892             const new_mut_ptr = Air.internedToRef(try pt.intern(.{ .ptr = .{
   3893                 .ty = alloc_ty.toIntern(),
   3894                 .base_addr = .{ .uav = .{
   3895                     .val = interned.toIntern(),
   3896                     .orig_ty = alloc_ty.toIntern(),
   3897                 } },
   3898                 .byte_offset = 0,
   3899             } }));
   3900             return sema.makePtrConst(block, new_mut_ptr);
   3901         }
   3902     }
   3903 
   3904     // Otherwise, check if the alloc is comptime-known despite being in a runtime scope.
   3905     if (try sema.resolveComptimeKnownAllocPtr(block, alloc, null)) |ptr_val| {
   3906         return sema.makePtrConst(block, Air.internedToRef(ptr_val));
   3907     }
   3908 
   3909     if (try elem_ty.comptimeOnlySema(pt)) {
   3910         // The value was initialized through RLS, so we didn't detect the runtime condition earlier.
   3911         // TODO: source location of runtime control flow
   3912         const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node });
   3913         return sema.fail(block, init_src, "value with comptime-only type '{f}' depends on runtime control flow", .{elem_ty.fmt(pt)});
   3914     }
   3915 
   3916     // This is a runtime value.
   3917     return sema.makePtrConst(block, alloc);
   3918 }
   3919 
   3920 /// If `alloc` is an inferred allocation, `resolved_inferred_ty` is taken to be its resolved
   3921 /// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`.
   3922 fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index {
   3923     const pt = sema.pt;
   3924     const zcu = pt.zcu;
   3925 
   3926     const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc);
   3927     const ptr_info = alloc_ty.ptrInfo(zcu);
   3928     const elem_ty: Type = .fromInterned(ptr_info.child);
   3929 
   3930     const alloc_inst = alloc.toIndex() orelse return null;
   3931     const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null;
   3932     const stores = comptime_info.value.stores.items(.inst);
   3933 
   3934     // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known.
   3935     // We will resolve and return its value.
   3936 
   3937     // We expect to have emitted at least one store, unless the elem type is OPV.
   3938     if (stores.len == 0) {
   3939         const val = (try sema.typeHasOnePossibleValue(elem_ty)).?.toIntern();
   3940         return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value);
   3941     }
   3942 
   3943     // In general, we want to create a comptime alloc of the correct type and
   3944     // apply the stores to that alloc in order. However, before going to all
   3945     // that effort, let's optimize for the common case of a single store.
   3946 
   3947     simple: {
   3948         if (stores.len != 1) break :simple;
   3949         const store_inst = sema.air_instructions.get(@intFromEnum(stores[0]));
   3950         switch (store_inst.tag) {
   3951             .store, .store_safe => {},
   3952             .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => break :simple, // there's OPV stuff going on!
   3953             else => unreachable,
   3954         }
   3955         if (store_inst.data.bin_op.lhs != alloc) break :simple;
   3956 
   3957         const val = store_inst.data.bin_op.rhs.toInterned().?;
   3958         assert(zcu.intern_pool.typeOf(val) == elem_ty.toIntern());
   3959         return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value);
   3960     }
   3961 
   3962     // The simple strategy failed: we must create a mutable comptime alloc and
   3963     // perform all of the runtime store operations at comptime.
   3964 
   3965     const ct_alloc = try sema.newComptimeAlloc(block, .unneeded, elem_ty, ptr_info.flags.alignment);
   3966 
   3967     const alloc_ptr = try pt.intern(.{ .ptr = .{
   3968         .ty = alloc_ty.toIntern(),
   3969         .base_addr = .{ .comptime_alloc = ct_alloc },
   3970         .byte_offset = 0,
   3971     } });
   3972 
   3973     // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the comptime alloc
   3974     var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena);
   3975     try ptr_mapping.ensureTotalCapacity(@intCast(stores.len));
   3976     ptr_mapping.putAssumeCapacity(alloc_inst, alloc_ptr);
   3977 
   3978     // Whilst constructing our mapping, we will also initialize optional and error union payloads when
   3979     // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters.
   3980     var to_map = try std.array_list.Managed(Air.Inst.Index).initCapacity(sema.arena, stores.len);
   3981 
   3982     for (stores) |store_inst_idx| {
   3983         const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
   3984         const ptr_to_map = switch (store_inst.tag) {
   3985             .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to.
   3986             .set_union_tag => store_inst.data.bin_op.lhs.toIndex().?, // Map the union pointer.
   3987             .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.
   3988             else => unreachable,
   3989         };
   3990         to_map.appendAssumeCapacity(ptr_to_map);
   3991     }
   3992 
   3993     const tmp_air = sema.getTmpAir();
   3994 
   3995     while (to_map.pop()) |air_ptr| {
   3996         if (ptr_mapping.contains(air_ptr)) continue;
   3997         const PointerMethod = union(enum) {
   3998             same_addr,
   3999             opt_payload,
   4000             eu_payload,
   4001             field: u32,
   4002             elem: u64,
   4003         };
   4004         const inst_tag = tmp_air.instructions.items(.tag)[@intFromEnum(air_ptr)];
   4005         const air_parent_ptr: Air.Inst.Ref, const method: PointerMethod = switch (inst_tag) {
   4006             .struct_field_ptr => blk: {
   4007                 const data = tmp_air.extraData(
   4008                     Air.StructField,
   4009                     tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload,
   4010                 ).data;
   4011                 break :blk .{
   4012                     data.struct_operand,
   4013                     .{ .field = data.field_index },
   4014                 };
   4015             },
   4016             .struct_field_ptr_index_0,
   4017             .struct_field_ptr_index_1,
   4018             .struct_field_ptr_index_2,
   4019             .struct_field_ptr_index_3,
   4020             => .{
   4021                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   4022                 .{ .field = switch (inst_tag) {
   4023                     .struct_field_ptr_index_0 => 0,
   4024                     .struct_field_ptr_index_1 => 1,
   4025                     .struct_field_ptr_index_2 => 2,
   4026                     .struct_field_ptr_index_3 => 3,
   4027                     else => unreachable,
   4028                 } },
   4029             },
   4030             .ptr_slice_ptr_ptr => .{
   4031                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   4032                 .{ .field = Value.slice_ptr_index },
   4033             },
   4034             .ptr_slice_len_ptr => .{
   4035                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   4036                 .{ .field = Value.slice_len_index },
   4037             },
   4038             .ptr_elem_ptr => blk: {
   4039                 const data = tmp_air.extraData(
   4040                     Air.Bin,
   4041                     tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload,
   4042                 ).data;
   4043                 const idx_val = (try sema.resolveValue(data.rhs)).?;
   4044                 break :blk .{
   4045                     data.lhs,
   4046                     .{ .elem = try idx_val.toUnsignedIntSema(pt) },
   4047                 };
   4048             },
   4049             .bitcast => .{
   4050                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   4051                 .same_addr,
   4052             },
   4053             .optional_payload_ptr_set => .{
   4054                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   4055                 .opt_payload,
   4056             },
   4057             .errunion_payload_ptr_set => .{
   4058                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   4059                 .eu_payload,
   4060             },
   4061             else => unreachable,
   4062         };
   4063 
   4064         const decl_parent_ptr = ptr_mapping.get(air_parent_ptr.toIndex().?) orelse {
   4065             // Resolve the parent pointer first.
   4066             // Note that we add in what seems like the wrong order, because we're popping from the end of this array.
   4067             try to_map.appendSlice(&.{ air_ptr, air_parent_ptr.toIndex().? });
   4068             continue;
   4069         };
   4070         const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &zcu.intern_pool).toIntern();
   4071         const new_ptr = switch (method) {
   4072             .same_addr => try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, decl_parent_ptr, new_ptr_ty),
   4073             .opt_payload => ptr: {
   4074                 // Set the optional to non-null at comptime.
   4075                 // If the payload is OPV, we must use that value instead of undef.
   4076                 const opt_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
   4077                 const payload_ty = opt_ty.optionalChild(zcu);
   4078                 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
   4079                 const opt_val = try pt.intern(.{ .opt = .{
   4080                     .ty = opt_ty.toIntern(),
   4081                     .val = payload_val.toIntern(),
   4082                 } });
   4083                 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(opt_val), opt_ty);
   4084                 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrOptPayload(pt)).toIntern();
   4085             },
   4086             .eu_payload => ptr: {
   4087                 // Set the error union to non-error at comptime.
   4088                 // If the payload is OPV, we must use that value instead of undef.
   4089                 const eu_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
   4090                 const payload_ty = eu_ty.errorUnionPayload(zcu);
   4091                 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
   4092                 const eu_val = try pt.intern(.{ .error_union = .{
   4093                     .ty = eu_ty.toIntern(),
   4094                     .val = .{ .payload = payload_val.toIntern() },
   4095                 } });
   4096                 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(eu_val), eu_ty);
   4097                 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrEuPayload(pt)).toIntern();
   4098             },
   4099             .field => |idx| ptr: {
   4100                 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
   4101                 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| {
   4102                     // As this is a union field, we must store to the pointer now to set the tag.
   4103                     // The payload value will be stored later, so undef is a sufficent payload for now.
   4104                     const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]);
   4105                     const payload_val = try pt.undefValue(payload_ty);
   4106                     const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx);
   4107                     const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);
   4108                     try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty);
   4109                 }
   4110                 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
   4111             },
   4112             .elem => |idx| (try Value.fromInterned(decl_parent_ptr).ptrElem(idx, pt)).toIntern(),
   4113         };
   4114         try ptr_mapping.put(air_ptr, new_ptr);
   4115     }
   4116 
   4117     // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime.
   4118     // Any implicit stores performed by `optional_payload_ptr_set` or `errunion_payload_ptr_set`
   4119     // instructions were already done above.
   4120 
   4121     for (stores) |store_inst_idx| {
   4122         const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
   4123         switch (store_inst.tag) {
   4124             .optional_payload_ptr_set, .errunion_payload_ptr_set => {}, // Handled explicitly above
   4125             .set_union_tag => {
   4126                 // Usually, we can ignore these, because the creation of the field pointer above
   4127                 // already did it for us. However, if the field is OPV, this is relevant, because
   4128                 // there is not going to be a store to the field. So we must initialize the union
   4129                 // tag if the field is OPV.
   4130                 const union_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
   4131                 const union_ptr_val: Value = .fromInterned(ptr_mapping.get(union_ptr_inst).?);
   4132                 const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?);
   4133                 const union_ty = union_ptr_val.typeOf(zcu).childType(zcu);
   4134                 const field_ty = union_ty.unionFieldType(tag_val, zcu).?;
   4135                 if (try sema.typeHasOnePossibleValue(field_ty)) |payload_val| {
   4136                     const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val);
   4137                     try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty);
   4138                 }
   4139             },
   4140             .store, .store_safe => {
   4141                 const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
   4142                 const store_val = (try sema.resolveValue(store_inst.data.bin_op.rhs)).?;
   4143                 const new_ptr = ptr_mapping.get(air_ptr_inst).?;
   4144                 try sema.storePtrVal(block, .unneeded, .fromInterned(new_ptr), store_val, store_val.typeOf(zcu));
   4145             },
   4146             else => unreachable,
   4147         }
   4148     }
   4149 
   4150     // The value is finalized - load it!
   4151     const val = (try sema.pointerDeref(block, LazySrcLoc.unneeded, Value.fromInterned(alloc_ptr), alloc_ty)).?.toIntern();
   4152     return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, ct_alloc, alloc_inst, comptime_info.value);
   4153 }
   4154 
   4155 /// Given the resolved comptime-known value, rewrites the dead AIR to not
   4156 /// create a runtime stack allocation. Also places the resulting value into
   4157 /// either an anon decl ref or a comptime alloc depending on whether it
   4158 /// references comptime-mutable memory. If `existing_comptime_alloc` is
   4159 /// passed, it is a scratch allocation which already contains `result_val`.
   4160 /// Same return type as `resolveComptimeKnownAllocPtr` so we can tail call.
   4161 fn finishResolveComptimeKnownAllocPtr(
   4162     sema: *Sema,
   4163     block: *Block,
   4164     alloc_ty: Type,
   4165     result_val: InternPool.Index,
   4166     existing_comptime_alloc: ?ComptimeAllocIndex,
   4167     alloc_inst: Air.Inst.Index,
   4168     comptime_info: MaybeComptimeAlloc,
   4169 ) CompileError!?InternPool.Index {
   4170     const pt = sema.pt;
   4171     const zcu = pt.zcu;
   4172 
   4173     // We're almost done - we have the resolved comptime value. We just need to
   4174     // eliminate the now-dead runtime instructions.
   4175 
   4176     // This instruction has type `alloc_ty`, meaning we can rewrite the `alloc` AIR instruction to
   4177     // this one to drop the side effect. We also need to rewrite the stores; we'll turn them to this
   4178     // too because it doesn't really matter what they become.
   4179     const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{
   4180         .ty = .fromIntern(alloc_ty.toIntern()),
   4181         .operand = .zero_usize,
   4182     } } };
   4183 
   4184     sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst);
   4185     for (comptime_info.stores.items(.inst)) |store_inst| {
   4186         sema.air_instructions.set(@intFromEnum(store_inst), nop_inst);
   4187     }
   4188 
   4189     if (Value.fromInterned(result_val).canMutateComptimeVarState(zcu)) {
   4190         const alloc_index = existing_comptime_alloc orelse a: {
   4191             const idx = try sema.newComptimeAlloc(block, .unneeded, alloc_ty.childType(zcu), alloc_ty.ptrAlignment(zcu));
   4192             const alloc = sema.getComptimeAlloc(idx);
   4193             alloc.val = .{ .interned = result_val };
   4194             break :a idx;
   4195         };
   4196         sema.getComptimeAlloc(alloc_index).is_const = true;
   4197         return try pt.intern(.{ .ptr = .{
   4198             .ty = alloc_ty.toIntern(),
   4199             .base_addr = .{ .comptime_alloc = alloc_index },
   4200             .byte_offset = 0,
   4201         } });
   4202     } else {
   4203         return try pt.intern(.{ .ptr = .{
   4204             .ty = alloc_ty.toIntern(),
   4205             .base_addr = .{ .uav = .{
   4206                 .orig_ty = alloc_ty.toIntern(),
   4207                 .val = result_val,
   4208             } },
   4209             .byte_offset = 0,
   4210         } });
   4211     }
   4212 }
   4213 
   4214 fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type {
   4215     var ptr_info = ptr_ty.ptrInfo(sema.pt.zcu);
   4216     ptr_info.flags.is_const = true;
   4217     return sema.pt.ptrTypeSema(ptr_info);
   4218 }
   4219 
   4220 fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   4221     const alloc_ty = sema.typeOf(alloc);
   4222     const const_ptr_ty = try sema.makePtrTyConst(alloc_ty);
   4223 
   4224     // Detect if a comptime value simply needs to have its type changed.
   4225     if (try sema.resolveValue(alloc)) |val| {
   4226         return Air.internedToRef((try sema.pt.getCoerced(val, const_ptr_ty)).toIntern());
   4227     }
   4228 
   4229     return block.addBitCast(const_ptr_ty, alloc);
   4230 }
   4231 
   4232 fn zirAllocInferredComptime(
   4233     sema: *Sema,
   4234     is_const: bool,
   4235 ) CompileError!Air.Inst.Ref {
   4236     const gpa = sema.gpa;
   4237 
   4238     try sema.air_instructions.append(gpa, .{
   4239         .tag = .inferred_alloc_comptime,
   4240         .data = .{ .inferred_alloc_comptime = .{
   4241             .alignment = .none,
   4242             .is_const = is_const,
   4243             .ptr = undefined,
   4244         } },
   4245     });
   4246     return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
   4247 }
   4248 
   4249 fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4250     const tracy = trace(@src());
   4251     defer tracy.end();
   4252 
   4253     const pt = sema.pt;
   4254 
   4255     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4256     const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
   4257     const var_src = block.nodeOffset(inst_data.src_node);
   4258 
   4259     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   4260     if (block.isComptime() or try var_ty.comptimeOnlySema(pt)) {
   4261         return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
   4262     }
   4263     if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
   4264         const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
   4265         return sema.fail(block, mut_src, "local variable in naked function", .{});
   4266     }
   4267     const target = pt.zcu.getTarget();
   4268     const ptr_type = try pt.ptrTypeSema(.{
   4269         .child = var_ty.toIntern(),
   4270         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   4271     });
   4272     const ptr = try block.addTy(.alloc, ptr_type);
   4273     const ptr_inst = ptr.toIndex().?;
   4274     try sema.maybe_comptime_allocs.put(sema.gpa, ptr_inst, .{ .runtime_index = block.runtime_index });
   4275     try sema.base_allocs.put(sema.gpa, ptr_inst, ptr_inst);
   4276     return ptr;
   4277 }
   4278 
   4279 fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4280     const tracy = trace(@src());
   4281     defer tracy.end();
   4282 
   4283     const pt = sema.pt;
   4284 
   4285     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4286     const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
   4287     const var_src = block.nodeOffset(inst_data.src_node);
   4288     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   4289     if (block.isComptime()) {
   4290         return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
   4291     }
   4292     if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
   4293         const store_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
   4294         return sema.fail(block, store_src, "local variable in naked function", .{});
   4295     }
   4296     try sema.validateVarType(block, ty_src, var_ty, false);
   4297     const target = pt.zcu.getTarget();
   4298     const ptr_type = try pt.ptrTypeSema(.{
   4299         .child = var_ty.toIntern(),
   4300         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   4301     });
   4302     return block.addTy(.alloc, ptr_type);
   4303 }
   4304 
   4305 fn zirAllocInferred(
   4306     sema: *Sema,
   4307     block: *Block,
   4308     is_const: bool,
   4309 ) CompileError!Air.Inst.Ref {
   4310     const tracy = trace(@src());
   4311     defer tracy.end();
   4312 
   4313     const gpa = sema.gpa;
   4314 
   4315     if (block.isComptime()) {
   4316         try sema.air_instructions.append(gpa, .{
   4317             .tag = .inferred_alloc_comptime,
   4318             .data = .{ .inferred_alloc_comptime = .{
   4319                 .alignment = .none,
   4320                 .is_const = is_const,
   4321                 .ptr = undefined,
   4322             } },
   4323         });
   4324         return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
   4325     }
   4326 
   4327     const result_index = try block.addInstAsIndex(.{
   4328         .tag = .inferred_alloc,
   4329         .data = .{ .inferred_alloc = .{
   4330             .alignment = .none,
   4331             .is_const = is_const,
   4332         } },
   4333     });
   4334     try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
   4335     if (is_const) {
   4336         try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index });
   4337         try sema.base_allocs.put(sema.gpa, result_index, result_index);
   4338     }
   4339     return result_index.toRef();
   4340 }
   4341 
   4342 fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4343     const tracy = trace(@src());
   4344     defer tracy.end();
   4345 
   4346     const pt = sema.pt;
   4347     const zcu = pt.zcu;
   4348     const gpa = sema.gpa;
   4349     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4350     const src = block.nodeOffset(inst_data.src_node);
   4351     const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
   4352     const ptr = try sema.resolveInst(inst_data.operand);
   4353     const ptr_inst = ptr.toIndex().?;
   4354     const target = zcu.getTarget();
   4355 
   4356     switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) {
   4357         .inferred_alloc_comptime => {
   4358             // The work was already done for us by `Sema.storeToInferredAllocComptime`.
   4359             // All we need to do is return the pointer.
   4360             const iac = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc_comptime;
   4361             const resolved_ptr = iac.ptr;
   4362 
   4363             if (std.debug.runtime_safety) {
   4364                 // The inferred_alloc_comptime should never be referenced again
   4365                 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ .tag = undefined, .data = undefined });
   4366             }
   4367 
   4368             const val = switch (zcu.intern_pool.indexToKey(resolved_ptr).ptr.base_addr) {
   4369                 .uav => |a| a.val,
   4370                 .comptime_alloc => |i| val: {
   4371                     const alloc = sema.getComptimeAlloc(i);
   4372                     break :val (try alloc.val.intern(pt, sema.arena)).toIntern();
   4373                 },
   4374                 else => unreachable,
   4375             };
   4376             if (zcu.intern_pool.isFuncBody(val)) {
   4377                 const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));
   4378                 if (try ty.fnHasRuntimeBitsSema(pt)) {
   4379                     const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(val);
   4380                     try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index }));
   4381                     try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
   4382                 }
   4383             }
   4384 
   4385             return Air.internedToRef(resolved_ptr);
   4386         },
   4387         .inferred_alloc => {
   4388             const ia1 = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc;
   4389             const ia2 = sema.unresolved_inferred_allocs.fetchSwapRemove(ptr_inst).?.value;
   4390             const peer_vals = try sema.arena.alloc(Air.Inst.Ref, ia2.prongs.items.len);
   4391             for (peer_vals, ia2.prongs.items) |*peer_val, store_inst| {
   4392                 assert(sema.air_instructions.items(.tag)[@intFromEnum(store_inst)] == .store);
   4393                 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
   4394                 peer_val.* = bin_op.rhs;
   4395             }
   4396             const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none);
   4397 
   4398             const final_ptr_ty = try pt.ptrTypeSema(.{
   4399                 .child = final_elem_ty.toIntern(),
   4400                 .flags = .{
   4401                     .alignment = ia1.alignment,
   4402                     .address_space = target_util.defaultAddressSpace(target, .local),
   4403                 },
   4404             });
   4405 
   4406             if (!ia1.is_const) {
   4407                 try sema.validateVarType(block, ty_src, final_elem_ty, false);
   4408             } else if (try sema.resolveComptimeKnownAllocPtr(block, ptr, final_ptr_ty)) |ptr_val| {
   4409                 const const_ptr_ty = try sema.makePtrTyConst(final_ptr_ty);
   4410                 const new_const_ptr = try pt.getCoerced(Value.fromInterned(ptr_val), const_ptr_ty);
   4411 
   4412                 // Unless the block is comptime, `alloc_inferred` always produces
   4413                 // a runtime constant. The final inferred type needs to be
   4414                 // fully resolved so it can be lowered in codegen.
   4415                 try final_elem_ty.resolveFully(pt);
   4416 
   4417                 return Air.internedToRef(new_const_ptr.toIntern());
   4418             }
   4419 
   4420             if (try final_elem_ty.comptimeOnlySema(pt)) {
   4421                 // The alloc wasn't comptime-known per the above logic, so the
   4422                 // type cannot be comptime-only.
   4423                 // TODO: source location of runtime control flow
   4424                 return sema.fail(block, src, "value with comptime-only type '{f}' depends on runtime control flow", .{final_elem_ty.fmt(pt)});
   4425             }
   4426             if (sema.func_is_naked and try final_elem_ty.hasRuntimeBitsSema(pt)) {
   4427                 const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
   4428                 return sema.fail(block, mut_src, "local variable in naked function", .{});
   4429             }
   4430             // Change it to a normal alloc.
   4431             sema.air_instructions.set(@intFromEnum(ptr_inst), .{
   4432                 .tag = .alloc,
   4433                 .data = .{ .ty = final_ptr_ty },
   4434             });
   4435 
   4436             // Now we need to go back over all the store instructions, and do the logic as if
   4437             // the new result ptr type was available.
   4438 
   4439             for (ia2.prongs.items) |placeholder_inst| {
   4440                 var replacement_block = block.makeSubBlock();
   4441                 defer replacement_block.instructions.deinit(gpa);
   4442 
   4443                 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .store);
   4444                 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].bin_op;
   4445                 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .store);
   4446 
   4447                 // If only one instruction is produced then we can replace the store
   4448                 // placeholder instruction with this instruction; no need for an entire block.
   4449                 if (replacement_block.instructions.items.len == 1) {
   4450                     const only_inst = replacement_block.instructions.items[0];
   4451                     sema.air_instructions.set(@intFromEnum(placeholder_inst), sema.air_instructions.get(@intFromEnum(only_inst)));
   4452                     continue;
   4453                 }
   4454 
   4455                 // Here we replace the placeholder store instruction with a block
   4456                 // that does the actual store logic.
   4457                 _ = try replacement_block.addBr(placeholder_inst, .void_value);
   4458                 try sema.air_extra.ensureUnusedCapacity(
   4459                     gpa,
   4460                     @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len,
   4461                 );
   4462                 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{
   4463                     .tag = .block,
   4464                     .data = .{ .ty_pl = .{
   4465                         .ty = .void_type,
   4466                         .payload = sema.addExtraAssumeCapacity(Air.Block{
   4467                             .body_len = @intCast(replacement_block.instructions.items.len),
   4468                         }),
   4469                     } },
   4470                 });
   4471                 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
   4472             }
   4473 
   4474             if (ia1.is_const) {
   4475                 return sema.makePtrConst(block, ptr);
   4476             } else {
   4477                 return ptr;
   4478             }
   4479         },
   4480         else => unreachable,
   4481     }
   4482 }
   4483 
   4484 fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4485     const pt = sema.pt;
   4486     const zcu = pt.zcu;
   4487     const gpa = sema.gpa;
   4488     const ip = &zcu.intern_pool;
   4489     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4490     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
   4491     const all_args = sema.code.refSlice(extra.end, extra.data.operands_len);
   4492     const arg_pairs: []const [2]Zir.Inst.Ref = @as([*]const [2]Zir.Inst.Ref, @ptrCast(all_args))[0..@divExact(all_args.len, 2)];
   4493     const src = block.nodeOffset(inst_data.src_node);
   4494 
   4495     var len: Air.Inst.Ref = .none;
   4496     var len_val: ?Value = null;
   4497     var len_idx: u32 = undefined;
   4498     var any_runtime = false;
   4499 
   4500     const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, arg_pairs.len);
   4501     defer gpa.free(runtime_arg_lens);
   4502 
   4503     // First pass to look for comptime values.
   4504     for (arg_pairs, 0..) |zir_arg_pair, i_usize| {
   4505         const i: u32 = @intCast(i_usize);
   4506         runtime_arg_lens[i] = .none;
   4507         if (zir_arg_pair[0] == .none) continue;
   4508 
   4509         const arg_src = block.src(.{ .for_input = .{
   4510             .for_node_offset = inst_data.src_node,
   4511             .input_index = i,
   4512         } });
   4513 
   4514         const arg_len_uncoerced = if (zir_arg_pair[1] == .none) l: {
   4515             // This argument is an indexable.
   4516             const object = try sema.resolveInst(zir_arg_pair[0]);
   4517             const object_ty = sema.typeOf(object);
   4518             if (!object_ty.isIndexable(zcu)) {
   4519                 // Instead of using checkIndexable we customize this error.
   4520                 const msg = msg: {
   4521                     const msg = try sema.errMsg(arg_src, "type '{f}' is not indexable and not a range", .{object_ty.fmt(pt)});
   4522                     errdefer msg.destroy(sema.gpa);
   4523                     try sema.errNote(arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{});
   4524 
   4525                     if (object_ty.zigTypeTag(zcu) == .error_union) {
   4526                         try sema.errNote(arg_src, msg, "consider using 'try', 'catch', or 'if'", .{});
   4527                     }
   4528 
   4529                     break :msg msg;
   4530                 };
   4531                 return sema.failWithOwnedErrorMsg(block, msg);
   4532             }
   4533             if (!object_ty.indexableHasLen(zcu)) continue;
   4534             break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), arg_src);
   4535         } else l: {
   4536             // This argument is a range.
   4537             const range_start = try sema.resolveInst(zir_arg_pair[0]);
   4538             const range_end = try sema.resolveInst(zir_arg_pair[1]);
   4539             break :l try sema.analyzeArithmetic(block, .sub, range_end, range_start, arg_src, arg_src, arg_src, true);
   4540         };
   4541         const arg_len = try sema.coerce(block, .usize, arg_len_uncoerced, arg_src);
   4542         if (len == .none) {
   4543             len = arg_len;
   4544             len_idx = i;
   4545         }
   4546         if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| {
   4547             if (len_val) |v| {
   4548                 if (!(try sema.valuesEqual(arg_val, v, .usize))) {
   4549                     const msg = msg: {
   4550                         const msg = try sema.errMsg(src, "non-matching for loop lengths", .{});
   4551                         errdefer msg.destroy(gpa);
   4552                         const a_src = block.src(.{ .for_input = .{
   4553                             .for_node_offset = inst_data.src_node,
   4554                             .input_index = len_idx,
   4555                         } });
   4556                         try sema.errNote(a_src, msg, "length {f} here", .{
   4557                             v.fmtValueSema(pt, sema),
   4558                         });
   4559                         try sema.errNote(arg_src, msg, "length {f} here", .{
   4560                             arg_val.fmtValueSema(pt, sema),
   4561                         });
   4562                         break :msg msg;
   4563                     };
   4564                     return sema.failWithOwnedErrorMsg(block, msg);
   4565                 }
   4566             } else {
   4567                 len = arg_len;
   4568                 len_val = arg_val;
   4569                 len_idx = i;
   4570             }
   4571             continue;
   4572         }
   4573         runtime_arg_lens[i] = arg_len;
   4574         any_runtime = true;
   4575     }
   4576 
   4577     if (len == .none) {
   4578         const msg = msg: {
   4579             const msg = try sema.errMsg(src, "unbounded for loop", .{});
   4580             errdefer msg.destroy(gpa);
   4581             for (arg_pairs, 0..) |zir_arg_pair, i_usize| {
   4582                 const i: u32 = @intCast(i_usize);
   4583                 if (zir_arg_pair[0] == .none) continue;
   4584                 if (zir_arg_pair[1] != .none) continue;
   4585                 const object = try sema.resolveInst(zir_arg_pair[0]);
   4586                 const object_ty = sema.typeOf(object);
   4587                 const arg_src = block.src(.{ .for_input = .{
   4588                     .for_node_offset = inst_data.src_node,
   4589                     .input_index = i,
   4590                 } });
   4591                 try sema.errNote(arg_src, msg, "type '{f}' has no upper bound", .{
   4592                     object_ty.fmt(pt),
   4593                 });
   4594             }
   4595             break :msg msg;
   4596         };
   4597         return sema.failWithOwnedErrorMsg(block, msg);
   4598     }
   4599 
   4600     // Now for the runtime checks.
   4601     if (any_runtime and block.wantSafety()) {
   4602         for (runtime_arg_lens, 0..) |arg_len, i| {
   4603             if (arg_len == .none) continue;
   4604             if (i == len_idx) continue;
   4605             const ok = try block.addBinOp(.cmp_eq, len, arg_len);
   4606             try sema.addSafetyCheck(block, src, ok, .for_len_mismatch);
   4607         }
   4608     }
   4609 
   4610     return len;
   4611 }
   4612 
   4613 /// Given any single pointer, retrieve a pointer to the payload of any optional
   4614 /// or error union pointed to, initializing these pointers along the way.
   4615 /// Given a `*E!?T`, returns a (valid) `*T`.
   4616 /// May invalidate already-stored payload data.
   4617 fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref {
   4618     const pt = sema.pt;
   4619     const zcu = pt.zcu;
   4620     var base_ptr = ptr;
   4621     while (true) switch (sema.typeOf(base_ptr).childType(zcu).zigTypeTag(zcu)) {
   4622         .error_union => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
   4623         .optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
   4624         else => break,
   4625     };
   4626     try sema.checkKnownAllocPtr(block, ptr, base_ptr);
   4627     return base_ptr;
   4628 }
   4629 
   4630 fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4631     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4632     const ptr = try sema.resolveInst(un_node.operand);
   4633     return sema.optEuBasePtrInit(block, ptr, block.nodeOffset(un_node.src_node));
   4634 }
   4635 
   4636 fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4637     const pt = sema.pt;
   4638     const zcu = pt.zcu;
   4639     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4640     const src = block.nodeOffset(pl_node.src_node);
   4641     const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data;
   4642     const uncoerced_val = try sema.resolveInst(extra.rhs);
   4643     const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.lhs) orelse return uncoerced_val;
   4644     const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu);
   4645     assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
   4646     const elem_ty = ptr_ty.childType(zcu);
   4647     switch (ptr_ty.ptrSize(zcu)) {
   4648         .one => {
   4649             const uncoerced_ty = sema.typeOf(uncoerced_val);
   4650             if (elem_ty.zigTypeTag(zcu) == .array and elem_ty.childType(zcu).toIntern() == uncoerced_ty.toIntern()) {
   4651                 // We're trying to initialize a *[1]T with a reference to a T - don't perform any coercion.
   4652                 return uncoerced_val;
   4653             }
   4654             // If the destination type is anyopaque, don't coerce - the pointer will coerce instead.
   4655             if (elem_ty.toIntern() == .anyopaque_type) {
   4656                 return uncoerced_val;
   4657             } else {
   4658                 return sema.coerce(block, elem_ty, uncoerced_val, src);
   4659             }
   4660         },
   4661         .slice, .many => {
   4662             // Our goal is to coerce `uncoerced_val` to an array of `elem_ty`.
   4663             const val_ty = sema.typeOf(uncoerced_val);
   4664             switch (val_ty.zigTypeTag(zcu)) {
   4665                 .array, .vector => {},
   4666                 else => if (!val_ty.isTuple(zcu)) {
   4667                     return sema.fail(block, src, "expected array of '{f}', found '{f}'", .{ elem_ty.fmt(pt), val_ty.fmt(pt) });
   4668                 },
   4669             }
   4670             const want_ty = try pt.arrayType(.{
   4671                 .len = val_ty.arrayLen(zcu),
   4672                 .child = elem_ty.toIntern(),
   4673                 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
   4674             });
   4675             return sema.coerce(block, want_ty, uncoerced_val, src);
   4676         },
   4677         .c => {
   4678             // There's nothing meaningful to do here, because we don't know if this is meant to be a
   4679             // single-pointer or a many-pointer.
   4680             return uncoerced_val;
   4681         },
   4682     }
   4683 }
   4684 
   4685 fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
   4686     const pt = sema.pt;
   4687     const zcu = pt.zcu;
   4688     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4689     const src = block.nodeOffset(un_node.src_node);
   4690 
   4691     const operand_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type;
   4692 
   4693     const payload_ty = if (is_ref) ty: {
   4694         if (!operand_ty.isSinglePointer(zcu)) {
   4695             return .generic_poison_type; // we can't get a meaningful result type here, since it will be `*E![n]T`, and we don't know `n`.
   4696         }
   4697         break :ty operand_ty.childType(zcu);
   4698     } else operand_ty;
   4699 
   4700     const err_set_ty: Type = err_set: {
   4701         // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals
   4702         // until we hit an error union or set.
   4703         var cur_ty = sema.fn_ret_ty;
   4704         while (true) {
   4705             switch (cur_ty.zigTypeTag(zcu)) {
   4706                 .error_set => break :err_set cur_ty,
   4707                 .error_union => break :err_set cur_ty.errorUnionSet(zcu),
   4708                 .optional => cur_ty = cur_ty.optionalChild(zcu),
   4709                 else => {
   4710                     // This function cannot return an error.
   4711                     // `try` is still valid if the error case is impossible, i.e. no error is returned.
   4712                     // So, the result type has an error set of `error{}`.
   4713                     break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(zcu.gpa, pt.tid, &.{}));
   4714                 },
   4715             }
   4716         }
   4717     };
   4718 
   4719     const eu_ty = try pt.errorUnionType(err_set_ty, payload_ty);
   4720 
   4721     if (is_ref) {
   4722         var ptr_info = operand_ty.ptrInfo(zcu);
   4723         ptr_info.child = eu_ty.toIntern();
   4724         const eu_ptr_ty = try pt.ptrTypeSema(ptr_info);
   4725         return Air.internedToRef(eu_ptr_ty.toIntern());
   4726     } else {
   4727         return Air.internedToRef(eu_ty.toIntern());
   4728     }
   4729 }
   4730 
   4731 fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   4732     const pt = sema.pt;
   4733     const zcu = pt.zcu;
   4734     const un_tok = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
   4735     const src = block.tokenOffset(un_tok.src_tok);
   4736     // In case of GenericPoison, we don't actually have a type, so this will be
   4737     // treated as an untyped address-of operator.
   4738     const ty_operand = try sema.resolveTypeOrPoison(block, src, un_tok.operand) orelse return;
   4739     if (ty_operand.optEuBaseType(zcu).zigTypeTag(zcu) != .pointer) {
   4740         return sema.failWithOwnedErrorMsg(block, msg: {
   4741             const msg = try sema.errMsg(src, "expected type '{f}', found pointer", .{ty_operand.fmt(pt)});
   4742             errdefer msg.destroy(sema.gpa);
   4743             try sema.errNote(src, msg, "address-of operator always returns a pointer", .{});
   4744             break :msg msg;
   4745         });
   4746     }
   4747 }
   4748 
   4749 fn zirValidateConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   4750     if (!block.isComptime()) return;
   4751 
   4752     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4753     const src = block.nodeOffset(un_node.src_node);
   4754     const init_ref = try sema.resolveInst(un_node.operand);
   4755     if (!try sema.isComptimeKnown(init_ref)) {
   4756         return sema.failWithNeededComptime(block, src, null);
   4757     }
   4758 }
   4759 
   4760 fn zirValidateArrayInitRefTy(
   4761     sema: *Sema,
   4762     block: *Block,
   4763     inst: Zir.Inst.Index,
   4764 ) CompileError!Air.Inst.Ref {
   4765     const pt = sema.pt;
   4766     const zcu = pt.zcu;
   4767     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4768     const src = block.nodeOffset(pl_node.src_node);
   4769     const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
   4770     const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.ptr_ty) orelse return .generic_poison_type;
   4771     const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu);
   4772     assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
   4773     switch (zcu.intern_pool.indexToKey(ptr_ty.toIntern())) {
   4774         .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
   4775             .slice, .many => {
   4776                 // Use array of correct length
   4777                 const arr_ty = try pt.arrayType(.{
   4778                     .len = extra.elem_count,
   4779                     .child = ptr_ty.childType(zcu).toIntern(),
   4780                     .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
   4781                 });
   4782                 return Air.internedToRef(arr_ty.toIntern());
   4783             },
   4784             else => {},
   4785         },
   4786         else => {},
   4787     }
   4788     // Otherwise, we just want the pointer child type
   4789     const ret_ty = ptr_ty.childType(zcu);
   4790     if (ret_ty.toIntern() == .anyopaque_type) {
   4791         // The actual array type is unknown, which we represent with a generic poison.
   4792         return .generic_poison_type;
   4793     }
   4794     const arr_ty = ret_ty.optEuBaseType(zcu);
   4795     try sema.validateArrayInitTy(block, src, src, extra.elem_count, arr_ty);
   4796     return Air.internedToRef(ret_ty.toIntern());
   4797 }
   4798 
   4799 fn zirValidateArrayInitTy(
   4800     sema: *Sema,
   4801     block: *Block,
   4802     inst: Zir.Inst.Index,
   4803     is_result_ty: bool,
   4804 ) CompileError!void {
   4805     const pt = sema.pt;
   4806     const zcu = pt.zcu;
   4807     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4808     const src = block.nodeOffset(inst_data.src_node);
   4809     const ty_src: LazySrcLoc = if (is_result_ty) src else block.src(.{ .node_offset_init_ty = inst_data.src_node });
   4810     const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
   4811     // It's okay for the type to be poison: this will result in an anonymous array init.
   4812     const ty = try sema.resolveTypeOrPoison(block, ty_src, extra.ty) orelse return;
   4813     const arr_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty;
   4814     return sema.validateArrayInitTy(block, src, ty_src, extra.init_count, arr_ty);
   4815 }
   4816 
   4817 fn validateArrayInitTy(
   4818     sema: *Sema,
   4819     block: *Block,
   4820     src: LazySrcLoc,
   4821     ty_src: LazySrcLoc,
   4822     init_count: u32,
   4823     ty: Type,
   4824 ) CompileError!void {
   4825     const pt = sema.pt;
   4826     const zcu = pt.zcu;
   4827     switch (ty.zigTypeTag(zcu)) {
   4828         .array => {
   4829             const array_len = ty.arrayLen(zcu);
   4830             if (init_count != array_len) {
   4831                 return sema.fail(block, src, "expected {d} array elements; found {d}", .{
   4832                     array_len, init_count,
   4833                 });
   4834             }
   4835             return;
   4836         },
   4837         .vector => {
   4838             const array_len = ty.arrayLen(zcu);
   4839             if (init_count != array_len) {
   4840                 return sema.fail(block, src, "expected {d} vector elements; found {d}", .{
   4841                     array_len, init_count,
   4842                 });
   4843             }
   4844             return;
   4845         },
   4846         .@"struct" => if (ty.isTuple(zcu)) {
   4847             try ty.resolveFields(pt);
   4848             const array_len = ty.arrayLen(zcu);
   4849             if (init_count > array_len) {
   4850                 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{
   4851                     array_len, init_count,
   4852                 });
   4853             }
   4854             return;
   4855         },
   4856         else => {},
   4857     }
   4858     return sema.failWithArrayInitNotSupported(block, ty_src, ty);
   4859 }
   4860 
   4861 fn zirValidateStructInitTy(
   4862     sema: *Sema,
   4863     block: *Block,
   4864     inst: Zir.Inst.Index,
   4865     is_result_ty: bool,
   4866 ) CompileError!void {
   4867     const pt = sema.pt;
   4868     const zcu = pt.zcu;
   4869     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4870     const src = block.nodeOffset(inst_data.src_node);
   4871     // It's okay for the type to be poison: this will result in an anonymous struct init.
   4872     const ty = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse return;
   4873     const struct_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty;
   4874 
   4875     switch (struct_ty.zigTypeTag(zcu)) {
   4876         .@"struct", .@"union" => return,
   4877         else => {},
   4878     }
   4879     return sema.failWithStructInitNotSupported(block, src, struct_ty);
   4880 }
   4881 
   4882 fn zirValidatePtrStructInit(
   4883     sema: *Sema,
   4884     block: *Block,
   4885     inst: Zir.Inst.Index,
   4886 ) CompileError!void {
   4887     const tracy = trace(@src());
   4888     defer tracy.end();
   4889 
   4890     const pt = sema.pt;
   4891     const zcu = pt.zcu;
   4892     const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4893     const init_src = block.nodeOffset(validate_inst.src_node);
   4894     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
   4895     const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
   4896     const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node;
   4897     const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4898     const object_ptr = try sema.resolveInst(field_ptr_extra.lhs);
   4899     const agg_ty = sema.typeOf(object_ptr).childType(zcu).optEuBaseType(zcu);
   4900     switch (agg_ty.zigTypeTag(zcu)) {
   4901         .@"struct" => return sema.validateStructInit(
   4902             block,
   4903             agg_ty,
   4904             init_src,
   4905             instrs,
   4906             object_ptr,
   4907         ),
   4908         .@"union" => return sema.validateUnionInit(
   4909             block,
   4910             agg_ty,
   4911             init_src,
   4912             instrs,
   4913         ),
   4914         else => unreachable,
   4915     }
   4916 }
   4917 
   4918 fn validateUnionInit(
   4919     sema: *Sema,
   4920     block: *Block,
   4921     union_ty: Type,
   4922     init_src: LazySrcLoc,
   4923     instrs: []const Zir.Inst.Index,
   4924 ) CompileError!void {
   4925     if (instrs.len == 1) {
   4926         // Trvial validation done, and the union tag was already set by machinery in `unionFieldPtr`.
   4927         return;
   4928     }
   4929     const msg = msg: {
   4930         const msg = try sema.errMsg(
   4931             init_src,
   4932             "cannot initialize multiple union fields at once; unions can only have one active field",
   4933             .{},
   4934         );
   4935         errdefer msg.destroy(sema.gpa);
   4936 
   4937         for (instrs[1..]) |inst| {
   4938             const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4939             const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node });
   4940             try sema.errNote(inst_src, msg, "additional initializer here", .{});
   4941         }
   4942         try sema.addDeclaredHereNote(msg, union_ty);
   4943         break :msg msg;
   4944     };
   4945     return sema.failWithOwnedErrorMsg(block, msg);
   4946 }
   4947 
   4948 fn validateStructInit(
   4949     sema: *Sema,
   4950     block: *Block,
   4951     struct_ty: Type,
   4952     init_src: LazySrcLoc,
   4953     instrs: []const Zir.Inst.Index,
   4954     struct_ptr: Air.Inst.Ref,
   4955 ) CompileError!void {
   4956     const pt = sema.pt;
   4957     const zcu = pt.zcu;
   4958     const gpa = sema.gpa;
   4959     const ip = &zcu.intern_pool;
   4960 
   4961     // Tracks whether each field was explicitly initialized.
   4962     const found_fields = try gpa.alloc(bool, struct_ty.structFieldCount(zcu));
   4963     defer gpa.free(found_fields);
   4964     @memset(found_fields, false);
   4965 
   4966     for (instrs) |field_ptr| {
   4967         const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
   4968         const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });
   4969         const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4970         const field_name = try ip.getOrPutString(
   4971             gpa,
   4972             pt.tid,
   4973             sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
   4974             .no_embedded_nulls,
   4975         );
   4976         const field_index = if (struct_ty.isTuple(zcu))
   4977             try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
   4978         else
   4979             try sema.structFieldIndex(block, struct_ty, field_name, field_src);
   4980         assert(found_fields[field_index] == false);
   4981         found_fields[field_index] = true;
   4982     }
   4983 
   4984     // Our job is simply to deal with default field values. Specifically, any field which was not
   4985     // explicitly initialized must have its default value stored to the field pointer, or, if the
   4986     // field has no default value, a compile error must be emitted instead.
   4987 
   4988     // In the past, this code had other responsibilities, which involved some nasty AIR rewrites. However,
   4989     // that work was actually all redundant:
   4990     //
   4991     // * If the struct value is comptime-known, field stores remain a perfectly valid way of initializing
   4992     //   the struct through RLS; there is no need to turn the field stores into one store. Comptime-known
   4993     //   consts are handled correctly either way thanks to `maybe_comptime_allocs` and friends.
   4994     //
   4995     // * If the struct type is comptime-only, we need to make sure all of the fields were comptime-known.
   4996     //   But the comptime-only type means that `struct_ptr` must be a comptime-mutable pointer, so the
   4997     //   field stores were to comptime-mutable pointers, so have already errored if not comptime-known.
   4998     //
   4999     // * If the value is runtime-known, then comptime-known fields must be validated as runtime values.
   5000     //   But this was already handled for every field store by the machinery in `checkComptimeKnownStore`.
   5001 
   5002     var root_msg: ?*Zcu.ErrorMsg = null;
   5003     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
   5004 
   5005     for (found_fields, 0..) |explicit, i_usize| {
   5006         if (explicit) continue;
   5007         const i: u32 = @intCast(i_usize);
   5008 
   5009         try struct_ty.resolveStructFieldInits(pt);
   5010         const default_val = struct_ty.structFieldDefaultValue(i, zcu);
   5011         if (default_val.toIntern() == .unreachable_value) {
   5012             const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
   5013                 const template = "missing tuple field with index {d}";
   5014                 if (root_msg) |msg| {
   5015                     try sema.errNote(init_src, msg, template, .{i});
   5016                 } else {
   5017                     root_msg = try sema.errMsg(init_src, template, .{i});
   5018                 }
   5019                 continue;
   5020             };
   5021             const template = "missing struct field: {f}";
   5022             const args = .{field_name.fmt(ip)};
   5023             if (root_msg) |msg| {
   5024                 try sema.errNote(init_src, msg, template, args);
   5025             } else {
   5026                 root_msg = try sema.errMsg(init_src, template, args);
   5027             }
   5028             continue;
   5029         }
   5030 
   5031         const field_src = init_src; // TODO better source location
   5032         const default_field_ptr = if (struct_ty.isTuple(zcu))
   5033             try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true)
   5034         else
   5035             try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
   5036         try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr);
   5037         try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store);
   5038     }
   5039 
   5040     if (root_msg) |msg| {
   5041         try sema.addDeclaredHereNote(msg, struct_ty);
   5042         root_msg = null;
   5043         return sema.failWithOwnedErrorMsg(block, msg);
   5044     }
   5045 }
   5046 
   5047 fn zirValidatePtrArrayInit(
   5048     sema: *Sema,
   5049     block: *Block,
   5050     inst: Zir.Inst.Index,
   5051 ) CompileError!void {
   5052     const pt = sema.pt;
   5053     const zcu = pt.zcu;
   5054     const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5055     const init_src = block.nodeOffset(validate_inst.src_node);
   5056     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
   5057     const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
   5058     const first_elem_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node;
   5059     const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data;
   5060     const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr);
   5061     const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu);
   5062     const array_len = array_ty.arrayLen(zcu);
   5063 
   5064     // Analagously to `validateStructInit`, our job is to handle default fields; either emitting AIR
   5065     // to initialize them, or emitting a compile error if an unspecified field has no default. For
   5066     // tuples, there are literally default field values, although they're guaranteed to be comptime
   5067     // fields so we don't need to initialize them. For arrays, we may have a sentinel, which is never
   5068     // specified so we always need to initialize here. For vectors, there's no such thing.
   5069 
   5070     switch (array_ty.zigTypeTag(zcu)) {
   5071         .@"struct" => if (instrs.len != array_len) {
   5072             var root_msg: ?*Zcu.ErrorMsg = null;
   5073             errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
   5074 
   5075             try array_ty.resolveStructFieldInits(pt);
   5076             var i = instrs.len;
   5077             while (i < array_len) : (i += 1) {
   5078                 const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern();
   5079                 if (default_val == .unreachable_value) {
   5080                     const template = "missing tuple field with index {d}";
   5081                     if (root_msg) |msg| {
   5082                         try sema.errNote(init_src, msg, template, .{i});
   5083                     } else {
   5084                         root_msg = try sema.errMsg(init_src, template, .{i});
   5085                     }
   5086                     continue;
   5087                 }
   5088             }
   5089 
   5090             if (root_msg) |msg| {
   5091                 root_msg = null;
   5092                 return sema.failWithOwnedErrorMsg(block, msg);
   5093             }
   5094         },
   5095 
   5096         .array => if (instrs.len != array_len) {
   5097             return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
   5098                 array_len, instrs.len,
   5099             });
   5100         } else if (array_ty.sentinel(zcu)) |sentinel| {
   5101             const array_len_ref = try pt.intRef(.usize, array_len);
   5102             const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true);
   5103             try sema.checkKnownAllocPtr(block, array_ptr, sentinel_ptr);
   5104             try sema.storePtr2(block, init_src, sentinel_ptr, init_src, .fromValue(sentinel), init_src, .store);
   5105         },
   5106 
   5107         .vector => if (instrs.len != array_len) {
   5108             return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
   5109                 array_len, instrs.len,
   5110             });
   5111         },
   5112 
   5113         else => unreachable,
   5114     }
   5115 }
   5116 
   5117 fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5118     const pt = sema.pt;
   5119     const zcu = pt.zcu;
   5120     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5121     const src = block.nodeOffset(inst_data.src_node);
   5122     const operand = try sema.resolveInst(inst_data.operand);
   5123     const operand_ty = sema.typeOf(operand);
   5124 
   5125     if (operand_ty.zigTypeTag(zcu) != .pointer) {
   5126         return sema.fail(block, src, "cannot dereference non-pointer type '{f}'", .{operand_ty.fmt(pt)});
   5127     } else switch (operand_ty.ptrSize(zcu)) {
   5128         .one, .c => {},
   5129         .many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{f}'", .{operand_ty.fmt(pt)}),
   5130         .slice => return sema.fail(block, src, "index syntax required for slice type '{f}'", .{operand_ty.fmt(pt)}),
   5131     }
   5132 
   5133     if ((try sema.typeHasOnePossibleValue(operand_ty.childType(zcu))) != null) {
   5134         // No need to validate the actual pointer value, we don't need it!
   5135         return;
   5136     }
   5137 
   5138     const elem_ty = operand_ty.elemType2(zcu);
   5139     if (try sema.resolveValue(operand)) |val| {
   5140         if (val.isUndef(zcu)) {
   5141             return sema.fail(block, src, "cannot dereference undefined value", .{});
   5142         }
   5143     } else if (try elem_ty.comptimeOnlySema(pt)) {
   5144         const msg = msg: {
   5145             const msg = try sema.errMsg(
   5146                 src,
   5147                 "values of type '{f}' must be comptime-known, but operand value is runtime-known",
   5148                 .{elem_ty.fmt(pt)},
   5149             );
   5150             errdefer msg.destroy(sema.gpa);
   5151 
   5152             try sema.explainWhyTypeIsComptime(msg, src, elem_ty);
   5153             break :msg msg;
   5154         };
   5155         return sema.failWithOwnedErrorMsg(block, msg);
   5156     }
   5157 }
   5158 
   5159 fn typeIsDestructurable(ty: Type, zcu: *const Zcu) bool {
   5160     return switch (ty.zigTypeTag(zcu)) {
   5161         .array, .vector => true,
   5162         .@"struct" => ty.isTuple(zcu),
   5163         else => false,
   5164     };
   5165 }
   5166 
   5167 fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5168     const pt = sema.pt;
   5169     const zcu = pt.zcu;
   5170     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5171     const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data;
   5172     const src = block.nodeOffset(inst_data.src_node);
   5173     const destructure_src = block.nodeOffset(extra.destructure_node);
   5174     const operand = try sema.resolveInst(extra.operand);
   5175     const operand_ty = sema.typeOf(operand);
   5176 
   5177     if (!typeIsDestructurable(operand_ty, zcu)) {
   5178         return sema.failWithOwnedErrorMsg(block, msg: {
   5179             const msg = try sema.errMsg(src, "type '{f}' cannot be destructured", .{operand_ty.fmt(pt)});
   5180             errdefer msg.destroy(sema.gpa);
   5181             try sema.errNote(destructure_src, msg, "result destructured here", .{});
   5182             if (operand_ty.zigTypeTag(pt.zcu) == .error_union) {
   5183                 const base_op_ty = operand_ty.errorUnionPayload(zcu);
   5184                 if (typeIsDestructurable(base_op_ty, zcu))
   5185                     try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   5186             }
   5187             break :msg msg;
   5188         });
   5189     }
   5190 
   5191     if (operand_ty.arrayLen(zcu) != extra.expect_len) {
   5192         return sema.failWithOwnedErrorMsg(block, msg: {
   5193             const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{
   5194                 extra.expect_len, operand_ty.arrayLen(zcu),
   5195             });
   5196             errdefer msg.destroy(sema.gpa);
   5197             try sema.errNote(destructure_src, msg, "result destructured here", .{});
   5198             break :msg msg;
   5199         });
   5200     }
   5201 }
   5202 
   5203 fn failWithBadMemberAccess(
   5204     sema: *Sema,
   5205     block: *Block,
   5206     agg_ty: Type,
   5207     field_src: LazySrcLoc,
   5208     field_name: InternPool.NullTerminatedString,
   5209 ) CompileError {
   5210     const pt = sema.pt;
   5211     const zcu = pt.zcu;
   5212     const ip = &zcu.intern_pool;
   5213     const kw_name = switch (agg_ty.zigTypeTag(zcu)) {
   5214         .@"union" => "union",
   5215         .@"struct" => "struct",
   5216         .@"opaque" => "opaque",
   5217         .@"enum" => "enum",
   5218         else => unreachable,
   5219     };
   5220     if (agg_ty.typeDeclInst(zcu)) |inst| if ((inst.resolve(ip) orelse return error.AnalysisFail) == .main_struct_inst) {
   5221         return sema.fail(block, field_src, "root source file struct '{f}' has no member named '{f}'", .{
   5222             agg_ty.fmt(pt), field_name.fmt(ip),
   5223         });
   5224     };
   5225 
   5226     return sema.fail(block, field_src, "{s} '{f}' has no member named '{f}'", .{
   5227         kw_name, agg_ty.fmt(pt), field_name.fmt(ip),
   5228     });
   5229 }
   5230 
   5231 fn failWithBadStructFieldAccess(
   5232     sema: *Sema,
   5233     block: *Block,
   5234     struct_ty: Type,
   5235     struct_type: InternPool.LoadedStructType,
   5236     field_src: LazySrcLoc,
   5237     field_name: InternPool.NullTerminatedString,
   5238 ) CompileError {
   5239     const pt = sema.pt;
   5240     const zcu = pt.zcu;
   5241     const ip = &zcu.intern_pool;
   5242 
   5243     const msg = msg: {
   5244         const msg = try sema.errMsg(
   5245             field_src,
   5246             "no field named '{f}' in struct '{f}'",
   5247             .{ field_name.fmt(ip), struct_type.name.fmt(ip) },
   5248         );
   5249         errdefer msg.destroy(sema.gpa);
   5250         try sema.errNote(struct_ty.srcLoc(zcu), msg, "struct declared here", .{});
   5251         break :msg msg;
   5252     };
   5253     return sema.failWithOwnedErrorMsg(block, msg);
   5254 }
   5255 
   5256 fn failWithBadUnionFieldAccess(
   5257     sema: *Sema,
   5258     block: *Block,
   5259     union_ty: Type,
   5260     union_obj: InternPool.LoadedUnionType,
   5261     field_src: LazySrcLoc,
   5262     field_name: InternPool.NullTerminatedString,
   5263 ) CompileError {
   5264     const pt = sema.pt;
   5265     const zcu = pt.zcu;
   5266     const ip = &zcu.intern_pool;
   5267     const gpa = sema.gpa;
   5268 
   5269     const msg = msg: {
   5270         const msg = try sema.errMsg(
   5271             field_src,
   5272             "no field named '{f}' in union '{f}'",
   5273             .{ field_name.fmt(ip), union_obj.name.fmt(ip) },
   5274         );
   5275         errdefer msg.destroy(gpa);
   5276         try sema.errNote(union_ty.srcLoc(zcu), msg, "union declared here", .{});
   5277         break :msg msg;
   5278     };
   5279     return sema.failWithOwnedErrorMsg(block, msg);
   5280 }
   5281 
   5282 fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void {
   5283     const zcu = sema.pt.zcu;
   5284     const src_loc = decl_ty.srcLocOrNull(zcu) orelse return;
   5285     const category = switch (decl_ty.zigTypeTag(zcu)) {
   5286         .@"union" => "union",
   5287         .@"struct" => "struct",
   5288         .@"enum" => "enum",
   5289         .@"opaque" => "opaque",
   5290         else => unreachable,
   5291     };
   5292     try sema.errNote(src_loc, parent, "{s} declared here", .{category});
   5293 }
   5294 
   5295 fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5296     const tracy = trace(@src());
   5297     defer tracy.end();
   5298 
   5299     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5300     const src = block.nodeOffset(pl_node.src_node);
   5301     const bin = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data;
   5302     const ptr = try sema.resolveInst(bin.lhs);
   5303     const operand = try sema.resolveInst(bin.rhs);
   5304     const ptr_inst = ptr.toIndex().?;
   5305     const air_datas = sema.air_instructions.items(.data);
   5306 
   5307     switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) {
   5308         .inferred_alloc_comptime => {
   5309             const iac = &air_datas[@intFromEnum(ptr_inst)].inferred_alloc_comptime;
   5310             return sema.storeToInferredAllocComptime(block, src, operand, iac);
   5311         },
   5312         .inferred_alloc => {
   5313             const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?;
   5314             return sema.storeToInferredAlloc(block, src, ptr, operand, ia);
   5315         },
   5316         else => unreachable,
   5317     }
   5318 }
   5319 
   5320 fn storeToInferredAlloc(
   5321     sema: *Sema,
   5322     block: *Block,
   5323     src: LazySrcLoc,
   5324     ptr: Air.Inst.Ref,
   5325     operand: Air.Inst.Ref,
   5326     inferred_alloc: *InferredAlloc,
   5327 ) CompileError!void {
   5328     // Create a store instruction as a placeholder.  This will be replaced by a
   5329     // proper store sequence once we know the stored type.
   5330     const dummy_store = try block.addBinOp(.store, ptr, operand);
   5331     try sema.checkComptimeKnownStore(block, dummy_store, src);
   5332     // Add the stored instruction to the set we will use to resolve peer types
   5333     // for the inferred allocation.
   5334     try inferred_alloc.prongs.append(sema.arena, dummy_store.toIndex().?);
   5335 }
   5336 
   5337 fn storeToInferredAllocComptime(
   5338     sema: *Sema,
   5339     block: *Block,
   5340     src: LazySrcLoc,
   5341     operand: Air.Inst.Ref,
   5342     iac: *Air.Inst.Data.InferredAllocComptime,
   5343 ) CompileError!void {
   5344     const pt = sema.pt;
   5345     const zcu = pt.zcu;
   5346     const operand_ty = sema.typeOf(operand);
   5347     // There will be only one store_to_inferred_ptr because we are running at comptime.
   5348     // The alloc will turn into a Decl or a ComptimeAlloc.
   5349     const operand_val = try sema.resolveValue(operand) orelse {
   5350         return sema.failWithNeededComptime(block, src, .{ .simple = .stored_to_comptime_var });
   5351     };
   5352     const alloc_ty = try pt.ptrTypeSema(.{
   5353         .child = operand_ty.toIntern(),
   5354         .flags = .{
   5355             .alignment = iac.alignment,
   5356             .is_const = iac.is_const,
   5357         },
   5358     });
   5359     if (iac.is_const and !operand_val.canMutateComptimeVarState(zcu)) {
   5360         iac.ptr = try pt.intern(.{ .ptr = .{
   5361             .ty = alloc_ty.toIntern(),
   5362             .base_addr = .{ .uav = .{
   5363                 .val = operand_val.toIntern(),
   5364                 .orig_ty = alloc_ty.toIntern(),
   5365             } },
   5366             .byte_offset = 0,
   5367         } });
   5368     } else {
   5369         const alloc_index = try sema.newComptimeAlloc(block, src, operand_ty, iac.alignment);
   5370         sema.getComptimeAlloc(alloc_index).val = .{ .interned = operand_val.toIntern() };
   5371         iac.ptr = try pt.intern(.{ .ptr = .{
   5372             .ty = alloc_ty.toIntern(),
   5373             .base_addr = .{ .comptime_alloc = alloc_index },
   5374             .byte_offset = 0,
   5375         } });
   5376     }
   5377 }
   5378 
   5379 fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5380     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5381     const src = block.nodeOffset(inst_data.src_node);
   5382     const quota: u32 = @intCast(try sema.resolveInt(block, src, inst_data.operand, .u32, .{ .simple = .operand_setEvalBranchQuota }));
   5383     sema.branch_quota = @max(sema.branch_quota, quota);
   5384     sema.allow_memoize = false;
   5385 }
   5386 
   5387 fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5388     const tracy = trace(@src());
   5389     defer tracy.end();
   5390 
   5391     const zir_tags = sema.code.instructions.items(.tag);
   5392     const zir_datas = sema.code.instructions.items(.data);
   5393     const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
   5394     const src = block.nodeOffset(inst_data.src_node);
   5395     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   5396     const ptr = try sema.resolveInst(extra.lhs);
   5397     const operand = try sema.resolveInst(extra.rhs);
   5398 
   5399     const is_ret = if (extra.lhs.toIndex()) |ptr_index|
   5400         zir_tags[@intFromEnum(ptr_index)] == .ret_ptr
   5401     else
   5402         false;
   5403 
   5404     const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
   5405     const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node });
   5406     const air_tag: Air.Inst.Tag = if (is_ret)
   5407         .ret_ptr
   5408     else if (block.wantSafety())
   5409         .store_safe
   5410     else
   5411         .store;
   5412     return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
   5413 }
   5414 
   5415 fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5416     const bytes = sema.code.instructions.items(.data)[@intFromEnum(inst)].str.get(sema.code);
   5417     return sema.addStrLit(
   5418         try sema.pt.zcu.intern_pool.getOrPutString(sema.gpa, sema.pt.tid, bytes, .maybe_embedded_nulls),
   5419         bytes.len,
   5420     );
   5421 }
   5422 
   5423 fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) CompileError!Air.Inst.Ref {
   5424     return sema.addStrLit(string.toString(), string.length(&sema.pt.zcu.intern_pool));
   5425 }
   5426 
   5427 pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref {
   5428     const pt = sema.pt;
   5429     const array_ty = try pt.arrayType(.{
   5430         .len = len,
   5431         .sentinel = .zero_u8,
   5432         .child = .u8_type,
   5433     });
   5434     const val = try pt.intern(.{ .aggregate = .{
   5435         .ty = array_ty.toIntern(),
   5436         .storage = .{ .bytes = string },
   5437     } });
   5438     return sema.uavRef(val);
   5439 }
   5440 
   5441 fn uavRef(sema: *Sema, val: InternPool.Index) CompileError!Air.Inst.Ref {
   5442     return Air.internedToRef(try sema.pt.refValue(val));
   5443 }
   5444 
   5445 fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5446     _ = block;
   5447     const tracy = trace(@src());
   5448     defer tracy.end();
   5449 
   5450     const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].int;
   5451     return sema.pt.intRef(.comptime_int, int);
   5452 }
   5453 
   5454 fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5455     _ = block;
   5456     const tracy = trace(@src());
   5457     defer tracy.end();
   5458 
   5459     const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].str;
   5460     const byte_count = int.len * @sizeOf(std.math.big.Limb);
   5461     const limb_bytes = sema.code.string_bytes[@intFromEnum(int.start)..][0..byte_count];
   5462 
   5463     // TODO: this allocation and copy is only needed because the limbs may be unaligned.
   5464     // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these
   5465     // two lines can be removed.
   5466     const limbs = try sema.arena.alloc(std.math.big.Limb, int.len);
   5467     @memcpy(mem.sliceAsBytes(limbs), limb_bytes);
   5468 
   5469     return Air.internedToRef((try sema.pt.intValue_big(.comptime_int, .{
   5470         .limbs = limbs,
   5471         .positive = true,
   5472     })).toIntern());
   5473 }
   5474 
   5475 fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5476     _ = block;
   5477     const number = sema.code.instructions.items(.data)[@intFromEnum(inst)].float;
   5478     return Air.internedToRef((try sema.pt.floatValue(
   5479         .comptime_float,
   5480         number,
   5481     )).toIntern());
   5482 }
   5483 
   5484 fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5485     _ = block;
   5486     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5487     const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
   5488     const number = extra.get();
   5489     return Air.internedToRef((try sema.pt.floatValue(.comptime_float, number)).toIntern());
   5490 }
   5491 
   5492 fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5493     const tracy = trace(@src());
   5494     defer tracy.end();
   5495 
   5496     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5497     const src = block.nodeOffset(inst_data.src_node);
   5498     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   5499     const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .compile_error_string });
   5500     return sema.fail(block, src, "{s}", .{msg});
   5501 }
   5502 
   5503 fn zirCompileLog(
   5504     sema: *Sema,
   5505     block: *Block,
   5506     extended: Zir.Inst.Extended.InstData,
   5507 ) CompileError!Air.Inst.Ref {
   5508     const pt = sema.pt;
   5509     const zcu = pt.zcu;
   5510     const gpa = zcu.gpa;
   5511 
   5512     var aw: std.Io.Writer.Allocating = .init(gpa);
   5513     defer aw.deinit();
   5514     const writer = &aw.writer;
   5515 
   5516     const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
   5517     const src_node = extra.data.src_node;
   5518     const args = sema.code.refSlice(extra.end, extended.small);
   5519 
   5520     for (args, 0..) |arg_ref, i| {
   5521         if (i != 0) writer.writeAll(", ") catch return error.OutOfMemory;
   5522 
   5523         const arg = try sema.resolveInst(arg_ref);
   5524         const arg_ty = sema.typeOf(arg);
   5525         if (try sema.resolveValueResolveLazy(arg)) |val| {
   5526             writer.print("@as({f}, {f})", .{
   5527                 arg_ty.fmt(pt), val.fmtValueSema(pt, sema),
   5528             }) catch return error.OutOfMemory;
   5529         } else {
   5530             writer.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory;
   5531         }
   5532     }
   5533 
   5534     const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, aw.written(), .no_embedded_nulls);
   5535 
   5536     const line_idx: Zcu.CompileLogLine.Index = if (zcu.free_compile_log_lines.pop()) |idx| idx: {
   5537         zcu.compile_log_lines.items[@intFromEnum(idx)] = .{
   5538             .next = .none,
   5539             .data = line_data,
   5540         };
   5541         break :idx idx;
   5542     } else idx: {
   5543         try zcu.compile_log_lines.append(gpa, .{
   5544             .next = .none,
   5545             .data = line_data,
   5546         });
   5547         break :idx @enumFromInt(zcu.compile_log_lines.items.len - 1);
   5548     };
   5549 
   5550     const gop = try zcu.compile_logs.getOrPut(gpa, sema.owner);
   5551     if (gop.found_existing) {
   5552         const prev_line = gop.value_ptr.last_line.get(zcu);
   5553         assert(prev_line.next == .none);
   5554         prev_line.next = line_idx.toOptional();
   5555         gop.value_ptr.last_line = line_idx;
   5556     } else {
   5557         gop.value_ptr.* = .{
   5558             .base_node_inst = block.src_base_inst,
   5559             .node_offset = src_node,
   5560             .first_line = line_idx,
   5561             .last_line = line_idx,
   5562         };
   5563     }
   5564     return .void_value;
   5565 }
   5566 
   5567 fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5568     const pt = sema.pt;
   5569     const zcu = pt.zcu;
   5570 
   5571     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5572     const src = block.nodeOffset(inst_data.src_node);
   5573     const msg_inst = try sema.resolveInst(inst_data.operand);
   5574 
   5575     const coerced_msg = try sema.coerce(block, .slice_const_u8, msg_inst, block.builtinCallArgSrc(inst_data.src_node, 0));
   5576 
   5577     if (block.isComptime()) {
   5578         return sema.fail(block, src, "encountered @panic at comptime", .{});
   5579     }
   5580 
   5581     // We only apply the first hint in a branch.
   5582     // This allows user-provided hints to override implicit cold hints.
   5583     if (sema.branch_hint == null) {
   5584         sema.branch_hint = .cold;
   5585     }
   5586 
   5587     if (!zcu.backendSupportsFeature(.panic_fn)) {
   5588         _ = try block.addNoOp(.trap);
   5589         return;
   5590     }
   5591 
   5592     try sema.ensureMemoizedStateResolved(src, .panic);
   5593     const panic_fn_index = zcu.builtin_decl_values.get(.@"panic.call");
   5594     const opt_usize_ty = try pt.optionalType(.usize_type);
   5595     const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{
   5596         .ty = opt_usize_ty.toIntern(),
   5597         .val = .none,
   5598     } })));
   5599     // `callBuiltin` also calls `addReferenceEntry` to the function body for us.
   5600     try sema.callBuiltin(
   5601         block,
   5602         src,
   5603         .fromIntern(panic_fn_index),
   5604         .auto,
   5605         &.{ coerced_msg, null_ret_addr },
   5606         .@"@panic",
   5607     );
   5608 }
   5609 
   5610 fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5611     const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node;
   5612     const src = block.nodeOffset(src_node);
   5613     if (block.isComptime())
   5614         return sema.fail(block, src, "encountered @trap at comptime", .{});
   5615     _ = try block.addNoOp(.trap);
   5616 }
   5617 
   5618 fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5619     const tracy = trace(@src());
   5620     defer tracy.end();
   5621 
   5622     const pt = sema.pt;
   5623     const zcu = pt.zcu;
   5624     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5625     const src = parent_block.nodeOffset(inst_data.src_node);
   5626     const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   5627     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   5628     const gpa = sema.gpa;
   5629 
   5630     // AIR expects a block outside the loop block too.
   5631     // Reserve space for a Loop instruction so that generated Break instructions can
   5632     // point to it, even if it doesn't end up getting used because the code ends up being
   5633     // comptime evaluated.
   5634     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   5635     const loop_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1);
   5636     try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
   5637     sema.air_instructions.appendAssumeCapacity(.{
   5638         .tag = .block,
   5639         .data = undefined,
   5640     });
   5641     sema.air_instructions.appendAssumeCapacity(.{
   5642         .tag = .loop,
   5643         .data = .{ .ty_pl = .{
   5644             .ty = .noreturn_type,
   5645             .payload = undefined,
   5646         } },
   5647     });
   5648     var label: Block.Label = .{
   5649         .zir_block = inst,
   5650         .merges = .{
   5651             .src_locs = .{},
   5652             .results = .{},
   5653             .br_list = .{},
   5654             .block_inst = block_inst,
   5655         },
   5656     };
   5657     var child_block = parent_block.makeSubBlock();
   5658     child_block.label = &label;
   5659     child_block.runtime_cond = null;
   5660     child_block.runtime_loop = src;
   5661     child_block.runtime_index.increment();
   5662     const merges = &child_block.label.?.merges;
   5663 
   5664     defer child_block.instructions.deinit(gpa);
   5665     defer merges.deinit(gpa);
   5666 
   5667     var loop_block = child_block.makeSubBlock();
   5668     defer loop_block.instructions.deinit(gpa);
   5669 
   5670     // Use `analyzeBodyInner` directly to push any comptime control flow up the stack.
   5671     try sema.analyzeBodyInner(&loop_block, body);
   5672 
   5673     // TODO: since AIR has `repeat` now, we could change ZIR to generate
   5674     // more optimal code utilizing `repeat` instructions across blocks!
   5675     // For now, if the generated loop body does not terminate `noreturn`,
   5676     // then `analyzeBodyInner` is signalling that it ended with `repeat`.
   5677 
   5678     const loop_block_len = loop_block.instructions.items.len;
   5679     if (loop_block_len > 0 and sema.typeOf(loop_block.instructions.items[loop_block_len - 1].toRef()).isNoReturn(zcu)) {
   5680         // If the loop ended with a noreturn terminator, then there is no way for it to loop,
   5681         // so we can just use the block instead.
   5682         try child_block.instructions.appendSlice(gpa, loop_block.instructions.items);
   5683     } else {
   5684         _ = try loop_block.addInst(.{
   5685             .tag = .repeat,
   5686             .data = .{ .repeat = .{
   5687                 .loop_inst = loop_inst,
   5688             } },
   5689         });
   5690         // Note that `loop_block_len` is now off by one.
   5691 
   5692         try child_block.instructions.append(gpa, loop_inst);
   5693 
   5694         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + loop_block_len + 1);
   5695         sema.air_instructions.items(.data)[@intFromEnum(loop_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(
   5696             Air.Block{ .body_len = @intCast(loop_block_len + 1) },
   5697         );
   5698         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items));
   5699     }
   5700     return sema.resolveAnalyzedBlock(parent_block, src, &child_block, merges, false);
   5701 }
   5702 
   5703 fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5704     const tracy = trace(@src());
   5705     defer tracy.end();
   5706 
   5707     const pt = sema.pt;
   5708     const zcu = pt.zcu;
   5709     const comp = zcu.comp;
   5710     const gpa = sema.gpa;
   5711     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5712     const src = parent_block.nodeOffset(pl_node.src_node);
   5713     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
   5714     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   5715 
   5716     // we check this here to avoid undefined symbols
   5717     if (!build_options.have_llvm)
   5718         return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{});
   5719 
   5720     var c_import_buf = std.array_list.Managed(u8).init(gpa);
   5721     defer c_import_buf.deinit();
   5722 
   5723     var child_block: Block = .{
   5724         .parent = parent_block,
   5725         .sema = sema,
   5726         .namespace = parent_block.namespace,
   5727         .instructions = .{},
   5728         .inlining = parent_block.inlining,
   5729         .comptime_reason = .{ .reason = .{
   5730             .src = src,
   5731             .r = .{ .simple = .operand_cImport },
   5732         } },
   5733         .c_import_buf = &c_import_buf,
   5734         .runtime_cond = parent_block.runtime_cond,
   5735         .runtime_loop = parent_block.runtime_loop,
   5736         .runtime_index = parent_block.runtime_index,
   5737         .src_base_inst = parent_block.src_base_inst,
   5738         .type_name_ctx = parent_block.type_name_ctx,
   5739     };
   5740     defer child_block.instructions.deinit(gpa);
   5741 
   5742     _ = try sema.analyzeInlineBody(&child_block, body, inst);
   5743 
   5744     var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err|
   5745         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5746     defer c_import_res.deinit(gpa);
   5747 
   5748     if (c_import_res.errors.errorMessageCount() != 0) {
   5749         const msg = msg: {
   5750             const msg = try sema.errMsg(src, "C import failed", .{});
   5751             errdefer msg.destroy(gpa);
   5752 
   5753             if (!comp.config.link_libc)
   5754                 try sema.errNote(src, msg, "libc headers not available; compilation does not link against libc", .{});
   5755 
   5756             const gop = try zcu.cimport_errors.getOrPut(gpa, sema.owner);
   5757             if (!gop.found_existing) {
   5758                 gop.value_ptr.* = c_import_res.errors;
   5759                 c_import_res.errors = std.zig.ErrorBundle.empty;
   5760             }
   5761             break :msg msg;
   5762         };
   5763         return sema.failWithOwnedErrorMsg(&child_block, msg);
   5764     }
   5765     const parent_mod = parent_block.ownerModule();
   5766     const digest = Cache.binToHex(c_import_res.digest);
   5767 
   5768     const new_file_index = file: {
   5769         const c_import_zig_path = try comp.arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ digest);
   5770         const c_import_mod = Package.Module.create(comp.arena, .{
   5771             .paths = .{
   5772                 .root = try .fromRoot(comp.arena, comp.dirs, .local_cache, c_import_zig_path),
   5773                 .root_src_path = "cimport.zig",
   5774             },
   5775             .fully_qualified_name = c_import_zig_path,
   5776             .cc_argv = parent_mod.cc_argv,
   5777             .inherited = .{},
   5778             .global = comp.config,
   5779             .parent = parent_mod,
   5780         }) catch |err| switch (err) {
   5781             error.OutOfMemory => |e| return e,
   5782             // None of these are possible because we are creating a package with
   5783             // the exact same configuration as the parent package, which already
   5784             // passed these checks.
   5785             error.ValgrindUnsupportedOnTarget => unreachable,
   5786             error.TargetRequiresSingleThreaded => unreachable,
   5787             error.BackendRequiresSingleThreaded => unreachable,
   5788             error.TargetRequiresPic => unreachable,
   5789             error.PieRequiresPic => unreachable,
   5790             error.DynamicLinkingRequiresPic => unreachable,
   5791             error.TargetHasNoRedZone => unreachable,
   5792             error.StackCheckUnsupportedByTarget => unreachable,
   5793             error.StackProtectorUnsupportedByTarget => unreachable,
   5794             error.StackProtectorUnavailableWithoutLibC => unreachable,
   5795         };
   5796         const c_import_file_path: Compilation.Path = try c_import_mod.root.join(gpa, comp.dirs, "cimport.zig");
   5797         errdefer c_import_file_path.deinit(gpa);
   5798         const c_import_file = try gpa.create(Zcu.File);
   5799         errdefer gpa.destroy(c_import_file);
   5800         const c_import_file_index = try zcu.intern_pool.createFile(gpa, pt.tid, .{
   5801             .bin_digest = c_import_file_path.digest(),
   5802             .file = c_import_file,
   5803             .root_type = .none,
   5804         });
   5805         c_import_file.* = .{
   5806             .status = .never_loaded,
   5807             .stat = undefined,
   5808             .is_builtin = false,
   5809             .path = c_import_file_path,
   5810             .source = null,
   5811             .tree = null,
   5812             .zir = null,
   5813             .zoir = null,
   5814             .mod = c_import_mod,
   5815             .sub_file_path = "cimport.zig",
   5816             .module_changed = false,
   5817             .prev_zir = null,
   5818             .zoir_invalidated = false,
   5819         };
   5820         break :file c_import_file_index;
   5821     };
   5822     pt.updateFile(new_file_index, zcu.fileByIndex(new_file_index)) catch |err|
   5823         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5824 
   5825     try pt.ensureFileAnalyzed(new_file_index);
   5826     const ty = zcu.fileRootType(new_file_index);
   5827     try sema.declareDependency(.{ .interned = ty });
   5828     try sema.addTypeReferenceEntry(src, ty);
   5829     return Air.internedToRef(ty);
   5830 }
   5831 
   5832 fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5833     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5834     const src = parent_block.nodeOffset(inst_data.src_node);
   5835     return sema.failWithUseOfAsync(parent_block, src);
   5836 }
   5837 
   5838 fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5839     const tracy = trace(@src());
   5840     defer tracy.end();
   5841 
   5842     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5843     const src = parent_block.nodeOffset(pl_node.src_node);
   5844     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
   5845     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   5846     const gpa = sema.gpa;
   5847 
   5848     // Reserve space for a Block instruction so that generated Break instructions can
   5849     // point to it, even if it doesn't end up getting used because the code ends up being
   5850     // comptime evaluated or is an unlabeled block.
   5851     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   5852     try sema.air_instructions.append(gpa, .{
   5853         .tag = .block,
   5854         .data = undefined,
   5855     });
   5856 
   5857     var label: Block.Label = .{
   5858         .zir_block = inst,
   5859         .merges = .{
   5860             .src_locs = .{},
   5861             .results = .{},
   5862             .br_list = .{},
   5863             .block_inst = block_inst,
   5864         },
   5865     };
   5866 
   5867     var child_block: Block = .{
   5868         .parent = parent_block,
   5869         .sema = sema,
   5870         .namespace = parent_block.namespace,
   5871         .instructions = .{},
   5872         .label = &label,
   5873         .inlining = parent_block.inlining,
   5874         .comptime_reason = parent_block.comptime_reason,
   5875         .is_typeof = parent_block.is_typeof,
   5876         .want_safety = parent_block.want_safety,
   5877         .float_mode = parent_block.float_mode,
   5878         .c_import_buf = parent_block.c_import_buf,
   5879         .runtime_cond = parent_block.runtime_cond,
   5880         .runtime_loop = parent_block.runtime_loop,
   5881         .runtime_index = parent_block.runtime_index,
   5882         .error_return_trace_index = parent_block.error_return_trace_index,
   5883         .src_base_inst = parent_block.src_base_inst,
   5884         .type_name_ctx = parent_block.type_name_ctx,
   5885     };
   5886 
   5887     defer child_block.instructions.deinit(gpa);
   5888     defer label.merges.deinit(gpa);
   5889 
   5890     return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
   5891 }
   5892 
   5893 /// Semantically analyze the given ZIR body, emitting any resulting runtime code into the AIR block
   5894 /// specified by `child_block` if necessary (and emitting this block into `parent_block`).
   5895 /// TODO: `merges` is known from `child_block`, remove this parameter.
   5896 fn resolveBlockBody(
   5897     sema: *Sema,
   5898     parent_block: *Block,
   5899     src: LazySrcLoc,
   5900     child_block: *Block,
   5901     body: []const Zir.Inst.Index,
   5902     /// This is the instruction that a break instruction within `body` can
   5903     /// use to return from the body.
   5904     body_inst: Zir.Inst.Index,
   5905     merges: *Block.Merges,
   5906 ) CompileError!Air.Inst.Ref {
   5907     if (child_block.isComptime()) {
   5908         return sema.resolveInlineBody(child_block, body, body_inst);
   5909     } else {
   5910         assert(sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)] == .block);
   5911         var need_debug_scope = false;
   5912         child_block.need_debug_scope = &need_debug_scope;
   5913         if (sema.analyzeBodyInner(child_block, body)) |_| {
   5914             return sema.resolveAnalyzedBlock(parent_block, src, child_block, merges, need_debug_scope);
   5915         } else |err| switch (err) {
   5916             error.ComptimeBreak => {
   5917                 // Comptime control flow is happening, however child_block may still contain
   5918                 // runtime instructions which need to be copied to the parent block.
   5919                 if (need_debug_scope and child_block.instructions.items.len > 0) {
   5920                     // We need a runtime block for scoping reasons.
   5921                     _ = try child_block.addBr(merges.block_inst, .void_value);
   5922                     try parent_block.instructions.append(sema.gpa, merges.block_inst);
   5923                     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).@"struct".fields.len +
   5924                         child_block.instructions.items.len);
   5925                     sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   5926                         .ty = .void_type,
   5927                         .payload = sema.addExtraAssumeCapacity(Air.Block{
   5928                             .body_len = @intCast(child_block.instructions.items.len),
   5929                         }),
   5930                     } };
   5931                     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
   5932                 } else {
   5933                     // We can copy instructions directly to the parent block.
   5934                     try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items);
   5935                 }
   5936 
   5937                 const break_inst = sema.comptime_break_inst;
   5938                 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
   5939                 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
   5940                 if (extra.block_inst == body_inst) {
   5941                     return try sema.resolveInst(break_data.operand);
   5942                 } else {
   5943                     return error.ComptimeBreak;
   5944                 }
   5945             },
   5946             else => |e| return e,
   5947         }
   5948     }
   5949 }
   5950 
   5951 /// After a body corresponding to an AIR `block` has been analyzed, this function places them into
   5952 /// the block pointed at by `merges.block_inst` if necessary, or the block may be elided in favor of
   5953 /// inlining the instructions directly into the parent block. Either way, it considers all merges of
   5954 /// this block, and combines them appropriately using peer type resolution, returning the final
   5955 /// value of the block.
   5956 fn resolveAnalyzedBlock(
   5957     sema: *Sema,
   5958     parent_block: *Block,
   5959     src: LazySrcLoc,
   5960     child_block: *Block,
   5961     merges: *Block.Merges,
   5962     need_debug_scope: bool,
   5963 ) CompileError!Air.Inst.Ref {
   5964     const tracy = trace(@src());
   5965     defer tracy.end();
   5966 
   5967     const gpa = sema.gpa;
   5968     const pt = sema.pt;
   5969     const zcu = pt.zcu;
   5970 
   5971     // Blocks must terminate with noreturn instruction.
   5972     assert(child_block.instructions.items.len != 0);
   5973     assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(zcu));
   5974 
   5975     const block_tag = sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)];
   5976     switch (block_tag) {
   5977         .block => {},
   5978         .dbg_inline_block => assert(need_debug_scope),
   5979         else => unreachable,
   5980     }
   5981     if (merges.results.items.len == 0) {
   5982         switch (block_tag) {
   5983             .block => {
   5984                 // No need for a block instruction. We can put the new instructions
   5985                 // directly into the parent block.
   5986                 if (need_debug_scope) {
   5987                     // The code following this block is unreachable, as the block has no
   5988                     // merges, so we don't necessarily need to emit this as an AIR block.
   5989                     // However, we need a block *somewhere* to make the scoping correct,
   5990                     // so forward this request to the parent block.
   5991                     if (parent_block.need_debug_scope) |ptr| ptr.* = true;
   5992                 }
   5993                 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
   5994                 return child_block.instructions.items[child_block.instructions.items.len - 1].toRef();
   5995             },
   5996             .dbg_inline_block => {
   5997                 // Create a block containing all instruction from the body.
   5998                 try parent_block.instructions.append(gpa, merges.block_inst);
   5999                 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len +
   6000                     child_block.instructions.items.len);
   6001                 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   6002                     .ty = .noreturn_type,
   6003                     .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
   6004                         .func = child_block.inlining.?.func,
   6005                         .body_len = @intCast(child_block.instructions.items.len),
   6006                     }),
   6007                 } };
   6008                 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
   6009                 return merges.block_inst.toRef();
   6010             },
   6011             else => unreachable,
   6012         }
   6013     }
   6014     if (merges.results.items.len == 1) {
   6015         // If the `break` is trailing, we may be able to elide the AIR block here
   6016         // by appending the new instructions directly to the parent block.
   6017         if (!need_debug_scope) {
   6018             const last_inst_index = child_block.instructions.items.len - 1;
   6019             const last_inst = child_block.instructions.items[last_inst_index];
   6020             if (sema.getBreakBlock(last_inst)) |br_block| {
   6021                 if (br_block == merges.block_inst) {
   6022                     // Great, the last instruction is the break! Put the instructions
   6023                     // directly into the parent block.
   6024                     try parent_block.instructions.appendSlice(gpa, child_block.instructions.items[0..last_inst_index]);
   6025                     return merges.results.items[0];
   6026                 }
   6027             }
   6028         }
   6029         // Okay, we need a runtime block. If the value is comptime-known, the
   6030         // block should just return void, and we return the merge result
   6031         // directly. Otherwise, we can defer to the logic below.
   6032         if (try sema.resolveValue(merges.results.items[0])) |result_val| {
   6033             // Create a block containing all instruction from the body.
   6034             try parent_block.instructions.append(gpa, merges.block_inst);
   6035             switch (block_tag) {
   6036                 .block => {
   6037                     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
   6038                         child_block.instructions.items.len);
   6039                     sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   6040                         .ty = .void_type,
   6041                         .payload = sema.addExtraAssumeCapacity(Air.Block{
   6042                             .body_len = @intCast(child_block.instructions.items.len),
   6043                         }),
   6044                     } };
   6045                 },
   6046                 .dbg_inline_block => {
   6047                     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len +
   6048                         child_block.instructions.items.len);
   6049                     sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   6050                         .ty = .void_type,
   6051                         .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
   6052                             .func = child_block.inlining.?.func,
   6053                             .body_len = @intCast(child_block.instructions.items.len),
   6054                         }),
   6055                     } };
   6056                 },
   6057                 else => unreachable,
   6058             }
   6059             sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
   6060             // Rewrite the break to just give value {}; the value is
   6061             // comptime-known and will be returned directly.
   6062             sema.air_instructions.items(.data)[@intFromEnum(merges.br_list.items[0])].br.operand = .void_value;
   6063             return Air.internedToRef(result_val.toIntern());
   6064         }
   6065     }
   6066     // It is impossible to have the number of results be > 1 in a comptime scope.
   6067     assert(!child_block.isComptime()); // Should already got a compile error in the condbr condition.
   6068 
   6069     // Note that we'll always create an AIR block here, so `need_debug_scope` is irrelevant.
   6070 
   6071     // Need to set the type and emit the Block instruction. This allows machine code generation
   6072     // to emit a jump instruction to after the block when it encounters the break.
   6073     try parent_block.instructions.append(gpa, merges.block_inst);
   6074     const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items });
   6075     // TODO add note "missing else causes void value"
   6076 
   6077     const type_src = src; // TODO: better source location
   6078     if (try resolved_ty.comptimeOnlySema(pt)) {
   6079         const msg = msg: {
   6080             const msg = try sema.errMsg(type_src, "value with comptime-only type '{f}' depends on runtime control flow", .{resolved_ty.fmt(pt)});
   6081             errdefer msg.destroy(sema.gpa);
   6082 
   6083             const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?;
   6084             try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
   6085 
   6086             try sema.explainWhyTypeIsComptime(msg, type_src, resolved_ty);
   6087 
   6088             break :msg msg;
   6089         };
   6090         return sema.failWithOwnedErrorMsg(child_block, msg);
   6091     }
   6092     for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| {
   6093         try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst);
   6094     }
   6095 
   6096     try sema.checkMergeAllowed(child_block, type_src, resolved_ty);
   6097 
   6098     const ty_inst = Air.internedToRef(resolved_ty.toIntern());
   6099     switch (block_tag) {
   6100         .block => {
   6101             try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
   6102                 child_block.instructions.items.len);
   6103             sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   6104                 .ty = ty_inst,
   6105                 .payload = sema.addExtraAssumeCapacity(Air.Block{
   6106                     .body_len = @intCast(child_block.instructions.items.len),
   6107                 }),
   6108             } };
   6109         },
   6110         .dbg_inline_block => {
   6111             try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len +
   6112                 child_block.instructions.items.len);
   6113             sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   6114                 .ty = ty_inst,
   6115                 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
   6116                     .func = child_block.inlining.?.func,
   6117                     .body_len = @intCast(child_block.instructions.items.len),
   6118                 }),
   6119             } };
   6120         },
   6121         else => unreachable,
   6122     }
   6123     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
   6124     // Now that the block has its type resolved, we need to go back into all the break
   6125     // instructions, and insert type coercion on the operands.
   6126     for (merges.br_list.items) |br| {
   6127         const br_operand = sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand;
   6128         const br_operand_src = src;
   6129         const br_operand_ty = sema.typeOf(br_operand);
   6130         if (br_operand_ty.eql(resolved_ty, zcu)) {
   6131             // No type coercion needed.
   6132             continue;
   6133         }
   6134         var coerce_block = parent_block.makeSubBlock();
   6135         defer coerce_block.instructions.deinit(gpa);
   6136         const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src);
   6137         // If no instructions were produced, such as in the case of a coercion of a
   6138         // constant value to a new type, we can simply point the br operand to it.
   6139         if (coerce_block.instructions.items.len == 0) {
   6140             sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand = coerced_operand;
   6141             continue;
   6142         }
   6143         assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1].toRef() == coerced_operand);
   6144 
   6145         // Convert the br instruction to a block instruction that has the coercion
   6146         // and then a new br inside that returns the coerced instruction.
   6147         const sub_block_len: u32 = @intCast(coerce_block.instructions.items.len + 1);
   6148         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
   6149             sub_block_len);
   6150         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
   6151         const sub_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   6152 
   6153         sema.air_instructions.items(.tag)[@intFromEnum(br)] = .block;
   6154         sema.air_instructions.items(.data)[@intFromEnum(br)] = .{ .ty_pl = .{
   6155             .ty = .noreturn_type,
   6156             .payload = sema.addExtraAssumeCapacity(Air.Block{
   6157                 .body_len = sub_block_len,
   6158             }),
   6159         } };
   6160         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items));
   6161         sema.air_extra.appendAssumeCapacity(@intFromEnum(sub_br_inst));
   6162 
   6163         sema.air_instructions.appendAssumeCapacity(.{
   6164             .tag = .br,
   6165             .data = .{ .br = .{
   6166                 .block_inst = merges.block_inst,
   6167                 .operand = coerced_operand,
   6168             } },
   6169         });
   6170     }
   6171 
   6172     if (try sema.typeHasOnePossibleValue(resolved_ty)) |block_only_value| {
   6173         return Air.internedToRef(block_only_value.toIntern());
   6174     }
   6175 
   6176     return merges.block_inst.toRef();
   6177 }
   6178 
   6179 fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6180     const tracy = trace(@src());
   6181     defer tracy.end();
   6182 
   6183     const pt = sema.pt;
   6184     const zcu = pt.zcu;
   6185     const ip = &zcu.intern_pool;
   6186     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   6187     const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
   6188 
   6189     const src = block.nodeOffset(inst_data.src_node);
   6190     const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   6191     const options_src = block.builtinCallArgSrc(inst_data.src_node, 1);
   6192 
   6193     const ptr = try sema.resolveInst(extra.exported);
   6194     const ptr_val = try sema.resolveConstDefinedValue(block, ptr_src, ptr, .{ .simple = .export_target });
   6195     const ptr_ty = ptr_val.typeOf(zcu);
   6196 
   6197     const options = try sema.resolveExportOptions(block, options_src, extra.options);
   6198 
   6199     {
   6200         if (ptr_ty.zigTypeTag(zcu) != .pointer) {
   6201             return sema.fail(block, ptr_src, "expected pointer type, found '{f}'", .{ptr_ty.fmt(pt)});
   6202         }
   6203         const ptr_ty_info = ptr_ty.ptrInfo(zcu);
   6204         if (ptr_ty_info.flags.size == .slice) {
   6205             return sema.fail(block, ptr_src, "export target cannot be slice", .{});
   6206         }
   6207         if (ptr_ty_info.packed_offset.host_size != 0) {
   6208             return sema.fail(block, ptr_src, "export target cannot be bit-pointer", .{});
   6209         }
   6210     }
   6211 
   6212     const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr;
   6213     switch (ptr_info.base_addr) {
   6214         .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}),
   6215         .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}),
   6216         .uav => |uav| {
   6217             if (ptr_info.byte_offset != 0) {
   6218                 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
   6219             }
   6220             if (options.linkage == .internal) return;
   6221             const export_ty = Value.fromInterned(uav.val).typeOf(zcu);
   6222             if (!try sema.validateExternType(export_ty, .other)) {
   6223                 return sema.failWithOwnedErrorMsg(block, msg: {
   6224                     const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)});
   6225                     errdefer msg.destroy(sema.gpa);
   6226                     try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other);
   6227                     try sema.addDeclaredHereNote(msg, export_ty);
   6228                     break :msg msg;
   6229                 });
   6230             }
   6231             try sema.exports.append(zcu.gpa, .{
   6232                 .opts = options,
   6233                 .src = src,
   6234                 .exported = .{ .uav = uav.val },
   6235                 .status = .in_progress,
   6236             });
   6237         },
   6238         .nav => |nav| {
   6239             if (ptr_info.byte_offset != 0) {
   6240                 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
   6241             }
   6242             try sema.analyzeExport(block, src, options, nav);
   6243         },
   6244     }
   6245 }
   6246 
   6247 pub fn analyzeExport(
   6248     sema: *Sema,
   6249     block: *Block,
   6250     src: LazySrcLoc,
   6251     options: Zcu.Export.Options,
   6252     orig_nav_index: InternPool.Nav.Index,
   6253 ) !void {
   6254     const gpa = sema.gpa;
   6255     const pt = sema.pt;
   6256     const zcu = pt.zcu;
   6257     const ip = &zcu.intern_pool;
   6258 
   6259     if (options.linkage == .internal)
   6260         return;
   6261 
   6262     try sema.ensureNavResolved(block, src, orig_nav_index, .fully);
   6263 
   6264     const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
   6265         .variable => |v| v.owner_nav,
   6266         .@"extern" => |e| e.owner_nav,
   6267         .func => |f| f.owner_nav,
   6268         else => orig_nav_index,
   6269     };
   6270 
   6271     const exported_nav = ip.getNav(exported_nav_index);
   6272     const export_ty: Type = .fromInterned(exported_nav.typeOf(ip));
   6273 
   6274     if (!try sema.validateExternType(export_ty, .other)) {
   6275         return sema.failWithOwnedErrorMsg(block, msg: {
   6276             const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)});
   6277             errdefer msg.destroy(gpa);
   6278 
   6279             try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other);
   6280 
   6281             try sema.addDeclaredHereNote(msg, export_ty);
   6282             break :msg msg;
   6283         });
   6284     }
   6285 
   6286     // TODO: some backends might support re-exporting extern decls
   6287     if (exported_nav.getExtern(ip) != null) {
   6288         return sema.fail(block, src, "export target cannot be extern", .{});
   6289     }
   6290 
   6291     try sema.maybeQueueFuncBodyAnalysis(block, src, exported_nav_index);
   6292 
   6293     try sema.exports.append(gpa, .{
   6294         .opts = options,
   6295         .src = src,
   6296         .exported = .{ .nav = exported_nav_index },
   6297         .status = .in_progress,
   6298     });
   6299 }
   6300 
   6301 fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
   6302     const pt = sema.pt;
   6303     const zcu = pt.zcu;
   6304     const ip = &zcu.intern_pool;
   6305     const func = switch (sema.owner.unwrap()) {
   6306         .func => |func| func,
   6307         .@"comptime",
   6308         .nav_val,
   6309         .nav_ty,
   6310         .type,
   6311         .memoized_state,
   6312         => return, // does nothing outside a function
   6313     };
   6314     ip.funcSetDisableInstrumentation(func);
   6315     sema.allow_memoize = false;
   6316 }
   6317 
   6318 fn zirDisableIntrinsics(sema: *Sema) CompileError!void {
   6319     const pt = sema.pt;
   6320     const zcu = pt.zcu;
   6321     const ip = &zcu.intern_pool;
   6322     const func = switch (sema.owner.unwrap()) {
   6323         .func => |func| func,
   6324         .@"comptime",
   6325         .nav_val,
   6326         .nav_ty,
   6327         .type,
   6328         .memoized_state,
   6329         => return, // does nothing outside a function
   6330     };
   6331     ip.funcSetDisableIntrinsics(func);
   6332     sema.allow_memoize = false;
   6333 }
   6334 
   6335 fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   6336     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   6337     const src = block.builtinCallArgSrc(extra.node, 0);
   6338     block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, .FloatMode, .{ .simple = .operand_setFloatMode });
   6339 }
   6340 
   6341 fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6342     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   6343     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   6344     block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, .{ .simple = .operand_setRuntimeSafety });
   6345 }
   6346 
   6347 fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6348     const tracy = trace(@src());
   6349     defer tracy.end();
   6350 
   6351     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break";
   6352     const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
   6353     const operand = try sema.resolveInst(inst_data.operand);
   6354     const zir_block = extra.block_inst;
   6355 
   6356     var block = start_block;
   6357     while (true) {
   6358         if (block.label) |label| {
   6359             if (label.zir_block == zir_block) {
   6360                 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
   6361                 const src_loc = if (extra.operand_src_node.unwrap()) |operand_src_node|
   6362                     start_block.nodeOffset(operand_src_node)
   6363                 else
   6364                     null;
   6365                 try label.merges.src_locs.append(sema.gpa, src_loc);
   6366                 try label.merges.results.append(sema.gpa, operand);
   6367                 try label.merges.br_list.append(sema.gpa, br_ref.toIndex().?);
   6368                 block.runtime_index.increment();
   6369                 if (block.runtime_cond == null and block.runtime_loop == null) {
   6370                     block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
   6371                     block.runtime_loop = start_block.runtime_loop;
   6372                 }
   6373                 return;
   6374             }
   6375         }
   6376         block = block.parent.?;
   6377     }
   6378 }
   6379 
   6380 fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6381     const tracy = trace(@src());
   6382     defer tracy.end();
   6383 
   6384     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break";
   6385     const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
   6386     const operand_src = start_block.nodeOffset(extra.operand_src_node.unwrap().?);
   6387     const uncoerced_operand = try sema.resolveInst(inst_data.operand);
   6388     const switch_inst = extra.block_inst;
   6389 
   6390     switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) {
   6391         .switch_block, .switch_block_ref => {},
   6392         else => unreachable, // assertion failure
   6393     }
   6394 
   6395     const switch_payload_index = sema.code.instructions.items(.data)[@intFromEnum(switch_inst)].pl_node.payload_index;
   6396     const switch_operand_ref = sema.code.extraData(Zir.Inst.SwitchBlock, switch_payload_index).data.operand;
   6397     const switch_operand_ty = sema.typeOf(try sema.resolveInst(switch_operand_ref));
   6398 
   6399     const operand = try sema.coerce(start_block, switch_operand_ty, uncoerced_operand, operand_src);
   6400 
   6401     try sema.validateRuntimeValue(start_block, operand_src, operand);
   6402 
   6403     // We want to generate a `switch_dispatch` instruction with the switch condition,
   6404     // possibly preceded by a store to the stack alloc containing the raw operand.
   6405     // However, to avoid too much special-case state in Sema, this is handled by the
   6406     // `switch` lowering logic. As such, we will find the `Block` corresponding to the
   6407     // parent `switch_block[_ref]` instruction, create a dummy `br`, and add a merge
   6408     // to signal to the switch logic to rewrite this into an appropriate dispatch.
   6409 
   6410     var block = start_block;
   6411     while (true) {
   6412         if (block.label) |label| {
   6413             if (label.zir_block == switch_inst) {
   6414                 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
   6415                 try label.merges.extra_insts.append(sema.gpa, br_ref.toIndex().?);
   6416                 try label.merges.extra_src_locs.append(sema.gpa, operand_src);
   6417                 block.runtime_index.increment();
   6418                 if (block.runtime_cond == null and block.runtime_loop == null) {
   6419                     block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
   6420                     block.runtime_loop = start_block.runtime_loop;
   6421                 }
   6422                 return;
   6423             }
   6424         }
   6425         block = block.parent.?;
   6426     }
   6427 }
   6428 
   6429 fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6430     if (block.isComptime() or block.ownerModule().strip) return;
   6431 
   6432     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
   6433 
   6434     if (block.instructions.items.len != 0) {
   6435         const idx = block.instructions.items[block.instructions.items.len - 1];
   6436         if (sema.air_instructions.items(.tag)[@intFromEnum(idx)] == .dbg_stmt) {
   6437             // The previous dbg_stmt didn't correspond to any actual code, so replace it.
   6438             sema.air_instructions.items(.data)[@intFromEnum(idx)].dbg_stmt = .{
   6439                 .line = inst_data.line,
   6440                 .column = inst_data.column,
   6441             };
   6442             return;
   6443         }
   6444     }
   6445 
   6446     _ = try block.addInst(.{
   6447         .tag = .dbg_stmt,
   6448         .data = .{ .dbg_stmt = .{
   6449             .line = inst_data.line,
   6450             .column = inst_data.column,
   6451         } },
   6452     });
   6453 }
   6454 
   6455 fn zirDbgEmptyStmt(_: *Sema, block: *Block, _: Zir.Inst.Index) CompileError!void {
   6456     if (block.isComptime() or block.ownerModule().strip) return;
   6457     _ = try block.addNoOp(.dbg_empty_stmt);
   6458 }
   6459 
   6460 fn zirDbgVar(
   6461     sema: *Sema,
   6462     block: *Block,
   6463     inst: Zir.Inst.Index,
   6464     air_tag: Air.Inst.Tag,
   6465 ) CompileError!void {
   6466     const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op;
   6467     const operand = try sema.resolveInst(str_op.operand);
   6468     const name = str_op.getStr(sema.code);
   6469     try sema.addDbgVar(block, operand, air_tag, name);
   6470 }
   6471 
   6472 fn addDbgVar(
   6473     sema: *Sema,
   6474     block: *Block,
   6475     operand: Air.Inst.Ref,
   6476     air_tag: Air.Inst.Tag,
   6477     name: []const u8,
   6478 ) CompileError!void {
   6479     if (block.isComptime() or block.ownerModule().strip) return;
   6480 
   6481     const pt = sema.pt;
   6482     const zcu = pt.zcu;
   6483     const operand_ty = sema.typeOf(operand);
   6484     const val_ty = switch (air_tag) {
   6485         .dbg_var_ptr => operand_ty.childType(zcu),
   6486         .dbg_var_val, .dbg_arg_inline => operand_ty,
   6487         else => unreachable,
   6488     };
   6489     if (try val_ty.comptimeOnlySema(pt)) return;
   6490     if (!(try val_ty.hasRuntimeBitsSema(pt))) return;
   6491     if (try sema.resolveValue(operand)) |operand_val| {
   6492         if (operand_val.canMutateComptimeVarState(zcu)) return;
   6493     }
   6494 
   6495     // To ensure the lexical scoping is known to backends, this alloc must be
   6496     // within a real runtime block. We set a flag which communicates information
   6497     // to the closest lexically enclosing block:
   6498     // * If it is a `block_inline`, communicates to logic in `analyzeBodyInner`
   6499     //   to create a post-hoc block.
   6500     // * Otherwise, communicates to logic in `resolveBlockBody` to create a
   6501     //   real `block` instruction.
   6502     if (block.need_debug_scope) |ptr| ptr.* = true;
   6503 
   6504     // Add the name to the AIR.
   6505     const name_nts = try sema.appendAirString(name);
   6506 
   6507     _ = try block.addInst(.{
   6508         .tag = air_tag,
   6509         .data = .{ .pl_op = .{
   6510             .payload = @intFromEnum(name_nts),
   6511             .operand = operand,
   6512         } },
   6513     });
   6514 }
   6515 
   6516 pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTerminatedString {
   6517     if (str.len == 0) return .none;
   6518     const nts: Air.NullTerminatedString = @enumFromInt(sema.air_extra.items.len);
   6519     const elements_used = str.len / 4 + 1;
   6520     const elements = try sema.air_extra.addManyAsSlice(sema.gpa, elements_used);
   6521     const buffer = mem.sliceAsBytes(elements);
   6522     @memcpy(buffer[0..str.len], str);
   6523     buffer[str.len] = 0;
   6524     return nts;
   6525 }
   6526 
   6527 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   6528     const pt = sema.pt;
   6529     const zcu = pt.zcu;
   6530     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   6531     const src = block.tokenOffset(inst_data.src_tok);
   6532     const decl_name = try zcu.intern_pool.getOrPutString(
   6533         sema.gpa,
   6534         pt.tid,
   6535         inst_data.get(sema.code),
   6536         .no_embedded_nulls,
   6537     );
   6538     const nav_index = try sema.lookupIdentifier(block, decl_name);
   6539     return sema.analyzeNavRef(block, src, nav_index);
   6540 }
   6541 
   6542 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   6543     const pt = sema.pt;
   6544     const zcu = pt.zcu;
   6545     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   6546     const src = block.tokenOffset(inst_data.src_tok);
   6547     const decl_name = try zcu.intern_pool.getOrPutString(
   6548         sema.gpa,
   6549         pt.tid,
   6550         inst_data.get(sema.code),
   6551         .no_embedded_nulls,
   6552     );
   6553     const nav = try sema.lookupIdentifier(block, decl_name);
   6554     return sema.analyzeNavVal(block, src, nav);
   6555 }
   6556 
   6557 fn lookupIdentifier(sema: *Sema, block: *Block, name: InternPool.NullTerminatedString) !InternPool.Nav.Index {
   6558     const pt = sema.pt;
   6559     const zcu = pt.zcu;
   6560     var namespace = block.namespace;
   6561     while (true) {
   6562         if (try sema.lookupInNamespace(block, namespace, name)) |lookup| {
   6563             assert(lookup.accessible);
   6564             return lookup.nav;
   6565         }
   6566         namespace = zcu.namespacePtr(namespace).parent.unwrap() orelse break;
   6567     }
   6568     unreachable; // AstGen detects use of undeclared identifiers.
   6569 }
   6570 
   6571 /// This looks up a member of a specific namespace.
   6572 fn lookupInNamespace(
   6573     sema: *Sema,
   6574     block: *Block,
   6575     namespace_index: InternPool.NamespaceIndex,
   6576     ident_name: InternPool.NullTerminatedString,
   6577 ) CompileError!?struct {
   6578     nav: InternPool.Nav.Index,
   6579     /// If `false`, the declaration is in a different file and is not `pub`.
   6580     /// We still return the declaration for better error reporting.
   6581     accessible: bool,
   6582 } {
   6583     const pt = sema.pt;
   6584     const zcu = pt.zcu;
   6585 
   6586     try pt.ensureNamespaceUpToDate(namespace_index);
   6587 
   6588     const namespace = zcu.namespacePtr(namespace_index);
   6589 
   6590     const adapter: Zcu.Namespace.NameAdapter = .{ .zcu = zcu };
   6591 
   6592     const src_file = zcu.namespacePtr(block.namespace).file_scope;
   6593 
   6594     if (Type.fromInterned(namespace.owner_type).typeDeclInst(zcu)) |type_decl_inst| {
   6595         try sema.declareDependency(.{ .namespace_name = .{
   6596             .namespace = type_decl_inst,
   6597             .name = ident_name,
   6598         } });
   6599     }
   6600 
   6601     if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
   6602         return .{
   6603             .nav = nav_index,
   6604             .accessible = true,
   6605         };
   6606     } else if (namespace.priv_decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
   6607         return .{
   6608             .nav = nav_index,
   6609             .accessible = src_file == namespace.file_scope,
   6610         };
   6611     }
   6612 
   6613     return null;
   6614 }
   6615 
   6616 fn funcDeclSrcInst(sema: *Sema, func_inst: Air.Inst.Ref) !?InternPool.TrackedInst.Index {
   6617     const pt = sema.pt;
   6618     const zcu = pt.zcu;
   6619     const ip = &zcu.intern_pool;
   6620     const func_val = try sema.resolveValue(func_inst) orelse return null;
   6621     if (func_val.isUndef(zcu)) return null;
   6622     const nav = switch (ip.indexToKey(func_val.toIntern())) {
   6623         .@"extern" => |e| e.owner_nav,
   6624         .func => |f| f.owner_nav,
   6625         .ptr => |ptr| switch (ptr.base_addr) {
   6626             .nav => |nav| if (ptr.byte_offset == 0) nav else return null,
   6627             else => return null,
   6628         },
   6629         else => return null,
   6630     };
   6631     return ip.getNav(nav).srcInst(ip);
   6632 }
   6633 
   6634 pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref {
   6635     const pt = sema.pt;
   6636     const zcu = pt.zcu;
   6637     const gpa = sema.gpa;
   6638 
   6639     if (block.isComptime() or block.is_typeof) {
   6640         const index_val = try pt.intValue_u64(.usize, sema.comptime_err_ret_trace.items.len);
   6641         return Air.internedToRef(index_val.toIntern());
   6642     }
   6643 
   6644     if (!block.ownerModule().error_tracing) return .none;
   6645 
   6646     const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
   6647     try stack_trace_ty.resolveFields(pt);
   6648     const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
   6649     const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) {
   6650         error.AnalysisFail => @panic("std.builtin.StackTrace is corrupt"),
   6651         error.ComptimeReturn, error.ComptimeBreak => unreachable,
   6652         error.OutOfMemory => |e| return e,
   6653     };
   6654 
   6655     return try block.addInst(.{
   6656         .tag = .save_err_return_trace_index,
   6657         .data = .{ .ty_pl = .{
   6658             .ty = Air.internedToRef(stack_trace_ty.toIntern()),
   6659             .payload = @intCast(field_index),
   6660         } },
   6661     });
   6662 }
   6663 
   6664 /// Add instructions to block to "pop" the error return trace.
   6665 /// If `operand` is provided, only pops if operand is non-error.
   6666 fn popErrorReturnTrace(
   6667     sema: *Sema,
   6668     block: *Block,
   6669     src: LazySrcLoc,
   6670     operand: Air.Inst.Ref,
   6671     saved_error_trace_index: Air.Inst.Ref,
   6672 ) CompileError!void {
   6673     const pt = sema.pt;
   6674     const zcu = pt.zcu;
   6675     const gpa = sema.gpa;
   6676     var is_non_error: ?bool = null;
   6677     var is_non_error_inst: Air.Inst.Ref = undefined;
   6678     if (operand != .none) {
   6679         is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand);
   6680         if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val|
   6681             is_non_error = cond_val.toBool();
   6682     } else is_non_error = true; // no operand means pop unconditionally
   6683 
   6684     if (is_non_error == true) {
   6685         // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or
   6686         // the result is comptime-known to be a non-error. Either way, pop unconditionally.
   6687 
   6688         const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace);
   6689         try stack_trace_ty.resolveFields(pt);
   6690         const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
   6691         const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
   6692         const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
   6693         const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true);
   6694         try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store);
   6695     } else if (is_non_error == null) {
   6696         // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need
   6697         // to pop any error trace that may have been propagated from our arguments.
   6698 
   6699         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len);
   6700         const cond_block_inst = try block.addInstAsIndex(.{
   6701             .tag = .block,
   6702             .data = .{
   6703                 .ty_pl = .{
   6704                     .ty = .void_type,
   6705                     .payload = undefined, // updated below
   6706                 },
   6707             },
   6708         });
   6709 
   6710         var then_block = block.makeSubBlock();
   6711         defer then_block.instructions.deinit(gpa);
   6712 
   6713         // If non-error, then pop the error return trace by restoring the index.
   6714         const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace);
   6715         try stack_trace_ty.resolveFields(pt);
   6716         const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
   6717         const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty);
   6718         const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
   6719         const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true);
   6720         try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store);
   6721         _ = try then_block.addBr(cond_block_inst, .void_value);
   6722 
   6723         // Otherwise, do nothing
   6724         var else_block = block.makeSubBlock();
   6725         defer else_block.instructions.deinit(gpa);
   6726         _ = try else_block.addBr(cond_block_inst, .void_value);
   6727 
   6728         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
   6729             then_block.instructions.items.len + else_block.instructions.items.len +
   6730             @typeInfo(Air.Block).@"struct".fields.len + 1); // +1 for the sole .cond_br instruction in the .block
   6731 
   6732         const cond_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   6733         try sema.air_instructions.append(gpa, .{
   6734             .tag = .cond_br,
   6735             .data = .{
   6736                 .pl_op = .{
   6737                     .operand = is_non_error_inst,
   6738                     .payload = sema.addExtraAssumeCapacity(Air.CondBr{
   6739                         .then_body_len = @intCast(then_block.instructions.items.len),
   6740                         .else_body_len = @intCast(else_block.instructions.items.len),
   6741                         .branch_hints = .{
   6742                             // Weight against error branch.
   6743                             .true = .likely,
   6744                             .false = .unlikely,
   6745                             // Code coverage is not valuable on either branch.
   6746                             .then_cov = .none,
   6747                             .else_cov = .none,
   6748                         },
   6749                     }),
   6750                 },
   6751             },
   6752         });
   6753         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
   6754         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
   6755 
   6756         sema.air_instructions.items(.data)[@intFromEnum(cond_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 });
   6757         sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst));
   6758     }
   6759 }
   6760 
   6761 fn zirCall(
   6762     sema: *Sema,
   6763     block: *Block,
   6764     inst: Zir.Inst.Index,
   6765     comptime kind: enum { direct, field },
   6766 ) CompileError!Air.Inst.Ref {
   6767     const tracy = trace(@src());
   6768     defer tracy.end();
   6769 
   6770     const pt = sema.pt;
   6771     const zcu = pt.zcu;
   6772     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   6773     const callee_src = block.src(.{ .node_offset_call_func = inst_data.src_node });
   6774     const call_src = block.nodeOffset(inst_data.src_node);
   6775     const ExtraType = switch (kind) {
   6776         .direct => Zir.Inst.Call,
   6777         .field => Zir.Inst.FieldCall,
   6778     };
   6779     const extra = sema.code.extraData(ExtraType, inst_data.payload_index);
   6780     const args_len = extra.data.flags.args_len;
   6781 
   6782     const modifier: std.builtin.CallModifier = @enumFromInt(extra.data.flags.packed_modifier);
   6783     const ensure_result_used = extra.data.flags.ensure_result_used;
   6784     const pop_error_return_trace = extra.data.flags.pop_error_return_trace;
   6785 
   6786     const callee: ResolvedFieldCallee = switch (kind) {
   6787         .direct => .{ .direct = try sema.resolveInst(extra.data.callee) },
   6788         .field => blk: {
   6789             const object_ptr = try sema.resolveInst(extra.data.obj_ptr);
   6790             const field_name = try zcu.intern_pool.getOrPutString(
   6791                 sema.gpa,
   6792                 pt.tid,
   6793                 sema.code.nullTerminatedString(extra.data.field_name_start),
   6794                 .no_embedded_nulls,
   6795             );
   6796             const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
   6797             break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src);
   6798         },
   6799     };
   6800     const func: Air.Inst.Ref = switch (callee) {
   6801         .direct => |func_inst| func_inst,
   6802         .method => |method| method.func_inst,
   6803     };
   6804 
   6805     const callee_ty = sema.typeOf(func);
   6806     const total_args = args_len + @intFromBool(callee == .method);
   6807     const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, callee == .method);
   6808 
   6809     // The block index before the call, so we can potentially insert an error trace save here later.
   6810     const block_index: Air.Inst.Index = @enumFromInt(block.instructions.items.len);
   6811 
   6812     // This will be set by `analyzeCall` to indicate whether any parameter was an error (making the
   6813     // error trace potentially dirty).
   6814     var input_is_error = false;
   6815 
   6816     const args_info: CallArgsInfo = .{ .zir_call = .{
   6817         .bound_arg = switch (callee) {
   6818             .direct => .none,
   6819             .method => |method| method.arg0_inst,
   6820         },
   6821         .bound_arg_src = callee_src,
   6822         .call_inst = inst,
   6823         .call_node_offset = inst_data.src_node,
   6824         .num_args = args_len,
   6825         .args_body = @ptrCast(sema.code.extra[extra.end..]),
   6826         .any_arg_is_error = &input_is_error,
   6827     } };
   6828 
   6829     // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction.
   6830     const call_dbg_node: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
   6831     const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
   6832 
   6833     if (block.ownerModule().error_tracing and
   6834         !block.isComptime() and !block.is_typeof and (input_is_error or pop_error_return_trace))
   6835     {
   6836         const return_ty = sema.typeOf(call_inst);
   6837         if (modifier != .always_tail and return_ty.isNoReturn(zcu))
   6838             return call_inst; // call to "fn (...) noreturn", don't pop
   6839 
   6840         // TODO: we don't fix up the error trace for always_tail correctly, we should be doing it
   6841         // *before* the recursive call. This will be a bit tricky to do and probably requires
   6842         // moving this logic into analyzeCall. But that's probably a good idea anyway.
   6843         if (modifier == .always_tail)
   6844             return call_inst;
   6845 
   6846         // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only
   6847         // need to clean-up our own trace if we were passed to a non-error-handling expression.
   6848         if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) {
   6849             const stack_trace_ty = try sema.getBuiltinType(call_src, .StackTrace);
   6850             try stack_trace_ty.resolveFields(pt);
   6851             const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "index", .no_embedded_nulls);
   6852             const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src);
   6853 
   6854             // Insert a save instruction before the arg resolution + call instructions we just generated
   6855             const save_inst = try block.insertInst(block_index, .{
   6856                 .tag = .save_err_return_trace_index,
   6857                 .data = .{ .ty_pl = .{
   6858                     .ty = Air.internedToRef(stack_trace_ty.toIntern()),
   6859                     .payload = @intCast(field_index),
   6860                 } },
   6861             });
   6862 
   6863             // Pop the error return trace, testing the result for non-error if necessary
   6864             const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst;
   6865             try sema.popErrorReturnTrace(block, call_src, operand, save_inst);
   6866         }
   6867 
   6868         return call_inst;
   6869     } else {
   6870         return call_inst;
   6871     }
   6872 }
   6873 
   6874 fn checkCallArgumentCount(
   6875     sema: *Sema,
   6876     block: *Block,
   6877     func: Air.Inst.Ref,
   6878     func_src: LazySrcLoc,
   6879     callee_ty: Type,
   6880     total_args: usize,
   6881     member_fn: bool,
   6882 ) !Type {
   6883     const pt = sema.pt;
   6884     const zcu = pt.zcu;
   6885     const func_ty: Type = func_ty: {
   6886         switch (callee_ty.zigTypeTag(zcu)) {
   6887             .@"fn" => break :func_ty callee_ty,
   6888             .pointer => {
   6889                 const ptr_info = callee_ty.ptrInfo(zcu);
   6890                 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") {
   6891                     break :func_ty .fromInterned(ptr_info.child);
   6892                 }
   6893             },
   6894             .optional => {
   6895                 const opt_child = callee_ty.optionalChild(zcu);
   6896                 if (opt_child.zigTypeTag(zcu) == .@"fn" or (opt_child.isSinglePointer(zcu) and
   6897                     opt_child.childType(zcu).zigTypeTag(zcu) == .@"fn"))
   6898                 {
   6899                     const msg = msg: {
   6900                         const msg = try sema.errMsg(func_src, "cannot call optional type '{f}'", .{
   6901                             callee_ty.fmt(pt),
   6902                         });
   6903                         errdefer msg.destroy(sema.gpa);
   6904                         try sema.errNote(func_src, msg, "consider using '.?', 'orelse' or 'if'", .{});
   6905                         break :msg msg;
   6906                     };
   6907                     return sema.failWithOwnedErrorMsg(block, msg);
   6908                 }
   6909             },
   6910             else => {},
   6911         }
   6912         return sema.fail(block, func_src, "type '{f}' not a function", .{callee_ty.fmt(pt)});
   6913     };
   6914 
   6915     const func_ty_info = zcu.typeToFunc(func_ty).?;
   6916     const fn_params_len = func_ty_info.param_types.len;
   6917     const args_len = total_args - @intFromBool(member_fn);
   6918     if (func_ty_info.is_var_args) {
   6919         assert(callConvSupportsVarArgs(func_ty_info.cc));
   6920         if (total_args >= fn_params_len) return func_ty;
   6921     } else if (fn_params_len == total_args) {
   6922         return func_ty;
   6923     }
   6924 
   6925     const maybe_func_inst = try sema.funcDeclSrcInst(func);
   6926     const member_str = if (member_fn) "member function " else "";
   6927     const variadic_str = if (func_ty_info.is_var_args) "at least " else "";
   6928     const msg = msg: {
   6929         const msg = try sema.errMsg(
   6930             func_src,
   6931             "{s}expected {s}{d} argument(s), found {d}",
   6932             .{
   6933                 member_str,
   6934                 variadic_str,
   6935                 fn_params_len - @intFromBool(member_fn),
   6936                 args_len,
   6937             },
   6938         );
   6939         errdefer msg.destroy(sema.gpa);
   6940 
   6941         if (maybe_func_inst) |func_inst| {
   6942             try sema.errNote(.{
   6943                 .base_node_inst = func_inst,
   6944                 .offset = LazySrcLoc.Offset.nodeOffset(.zero),
   6945             }, msg, "function declared here", .{});
   6946         }
   6947         break :msg msg;
   6948     };
   6949     return sema.failWithOwnedErrorMsg(block, msg);
   6950 }
   6951 
   6952 fn callBuiltin(
   6953     sema: *Sema,
   6954     block: *Block,
   6955     call_src: LazySrcLoc,
   6956     builtin_fn: Air.Inst.Ref,
   6957     modifier: std.builtin.CallModifier,
   6958     args: []const Air.Inst.Ref,
   6959     operation: CallOperation,
   6960 ) !void {
   6961     const pt = sema.pt;
   6962     const zcu = pt.zcu;
   6963     const callee_ty = sema.typeOf(builtin_fn);
   6964     const func_ty: Type = func_ty: {
   6965         switch (callee_ty.zigTypeTag(zcu)) {
   6966             .@"fn" => break :func_ty callee_ty,
   6967             .pointer => {
   6968                 const ptr_info = callee_ty.ptrInfo(zcu);
   6969                 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") {
   6970                     break :func_ty .fromInterned(ptr_info.child);
   6971                 }
   6972             },
   6973             else => {},
   6974         }
   6975         std.debug.panic("type '{f}' is not a function calling builtin fn", .{callee_ty.fmt(pt)});
   6976     };
   6977 
   6978     const func_ty_info = zcu.typeToFunc(func_ty).?;
   6979     const fn_params_len = func_ty_info.param_types.len;
   6980     if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) {
   6981         std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len });
   6982     }
   6983 
   6984     _ = try sema.analyzeCall(
   6985         block,
   6986         builtin_fn,
   6987         func_ty,
   6988         call_src,
   6989         call_src,
   6990         modifier,
   6991         false,
   6992         .{ .resolved = .{ .src = call_src, .args = args } },
   6993         null,
   6994         operation,
   6995     );
   6996 }
   6997 
   6998 const CallOperation = enum {
   6999     call,
   7000     @"@call",
   7001     @"@panic",
   7002     @"safety check",
   7003     @"error return",
   7004 };
   7005 
   7006 const CallArgsInfo = union(enum) {
   7007     /// The full list of resolved (but uncoerced) arguments is known ahead of time.
   7008     resolved: struct {
   7009         src: LazySrcLoc,
   7010         args: []const Air.Inst.Ref,
   7011     },
   7012 
   7013     /// The list of resolved (but uncoerced) arguments is known ahead of time, but
   7014     /// originated from a usage of the @call builtin at the given node offset.
   7015     call_builtin: struct {
   7016         call_node_offset: std.zig.Ast.Node.Offset,
   7017         args: []const Air.Inst.Ref,
   7018     },
   7019 
   7020     /// This call corresponds to a ZIR call instruction. The arguments have not yet been
   7021     /// resolved. They must be resolved by `analyzeCall` so that argument resolution and
   7022     /// generic instantiation may be interleaved. This is required for RLS to work on
   7023     /// generic parameters.
   7024     zir_call: struct {
   7025         /// This may be `none`, in which case it is ignored. Otherwise, it is the
   7026         /// already-resolved value of the first argument, from method call syntax.
   7027         bound_arg: Air.Inst.Ref,
   7028         /// The source location of `bound_arg` if it is not `null`. Otherwise `undefined`.
   7029         bound_arg_src: LazySrcLoc,
   7030         /// The ZIR call instruction. The parameter type is placed at this index while
   7031         /// analyzing arguments.
   7032         call_inst: Zir.Inst.Index,
   7033         /// The node offset of `call_inst`.
   7034         call_node_offset: std.zig.Ast.Node.Offset,
   7035         /// The number of arguments to this call, not including `bound_arg`.
   7036         num_args: u32,
   7037         /// The ZIR corresponding to all function arguments (other than `bound_arg`, if it
   7038         /// is not `none`). Format is precisely the same as trailing data of ZIR `call`.
   7039         args_body: []const Zir.Inst.Index,
   7040         /// This bool will be set to true if any argument evaluated turns out to have an error set or error union type.
   7041         /// This is used by the caller to restore the error return trace when necessary.
   7042         any_arg_is_error: *bool,
   7043     },
   7044 
   7045     fn count(cai: CallArgsInfo) usize {
   7046         return switch (cai) {
   7047             inline .resolved, .call_builtin => |resolved| resolved.args.len,
   7048             .zir_call => |zir_call| zir_call.num_args + @intFromBool(zir_call.bound_arg != .none),
   7049         };
   7050     }
   7051 
   7052     fn argSrc(cai: CallArgsInfo, block: *Block, arg_index: usize) LazySrcLoc {
   7053         return switch (cai) {
   7054             .resolved => |resolved| resolved.src,
   7055             .call_builtin => |call_builtin| block.src(.{ .call_arg = .{
   7056                 .call_node_offset = call_builtin.call_node_offset,
   7057                 .arg_index = @intCast(arg_index),
   7058             } }),
   7059             .zir_call => |zir_call| if (arg_index == 0 and zir_call.bound_arg != .none) {
   7060                 return zir_call.bound_arg_src;
   7061             } else block.src(.{ .call_arg = .{
   7062                 .call_node_offset = zir_call.call_node_offset,
   7063                 .arg_index = @intCast(arg_index - @intFromBool(zir_call.bound_arg != .none)),
   7064             } }),
   7065         };
   7066     }
   7067 
   7068     /// Analyzes the arg at `arg_index` and coerces it to `param_ty`.
   7069     /// `param_ty` may be `generic_poison`. A value of `null` indicates a varargs parameter.
   7070     /// `func_ty_info` may be the type before instantiation, even if a generic instantiation is in progress.
   7071     /// Emits a compile error if the argument is not comptime-known despite either `block.isComptime()` or
   7072     /// the parameter being marked `comptime`.
   7073     fn analyzeArg(
   7074         cai: CallArgsInfo,
   7075         sema: *Sema,
   7076         block: *Block,
   7077         arg_index: usize,
   7078         maybe_param_ty: ?Type,
   7079         func_ty_info: InternPool.Key.FuncType,
   7080         func_inst: Air.Inst.Ref,
   7081         maybe_func_src_inst: ?InternPool.TrackedInst.Index,
   7082     ) CompileError!Air.Inst.Ref {
   7083         const pt = sema.pt;
   7084         const zcu = pt.zcu;
   7085         const param_count = func_ty_info.param_types.len;
   7086         const uncoerced_arg: Air.Inst.Ref = switch (cai) {
   7087             inline .resolved, .call_builtin => |resolved| resolved.args[arg_index],
   7088             .zir_call => |zir_call| arg_val: {
   7089                 const has_bound_arg = zir_call.bound_arg != .none;
   7090                 if (arg_index == 0 and has_bound_arg) {
   7091                     break :arg_val zir_call.bound_arg;
   7092                 }
   7093                 const real_arg_idx = arg_index - @intFromBool(has_bound_arg);
   7094 
   7095                 const arg_body = if (real_arg_idx == 0) blk: {
   7096                     const start = zir_call.num_args;
   7097                     const end = @intFromEnum(zir_call.args_body[0]);
   7098                     break :blk zir_call.args_body[start..end];
   7099                 } else blk: {
   7100                     const start = @intFromEnum(zir_call.args_body[real_arg_idx - 1]);
   7101                     const end = @intFromEnum(zir_call.args_body[real_arg_idx]);
   7102                     break :blk zir_call.args_body[start..end];
   7103                 };
   7104 
   7105                 // Generate args to comptime params in comptime block
   7106                 const parent_comptime = block.comptime_reason;
   7107                 defer block.comptime_reason = parent_comptime;
   7108                 // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx`
   7109                 if (std.math.cast(u5, arg_index)) |i| {
   7110                     if (i < param_count and func_ty_info.paramIsComptime(i)) {
   7111                         block.comptime_reason = .{
   7112                             .reason = .{
   7113                                 .src = cai.argSrc(block, arg_index),
   7114                                 .r = .{
   7115                                     .comptime_param = .{
   7116                                         .comptime_src = if (maybe_func_src_inst) |src_inst| .{
   7117                                             .base_node_inst = src_inst,
   7118                                             .offset = .{ .func_decl_param_comptime = @intCast(arg_index) },
   7119                                         } else unreachable, // should be non-null because the function is generic
   7120                                     },
   7121                                 },
   7122                             },
   7123                         };
   7124                     }
   7125                 }
   7126                 // Give the arg its result type
   7127                 const provide_param_ty: Type = maybe_param_ty orelse .generic_poison;
   7128                 sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(provide_param_ty.toIntern()));
   7129                 // Resolve the arg!
   7130                 const uncoerced_arg = try sema.resolveInlineBody(block, arg_body, zir_call.call_inst);
   7131 
   7132                 if (block.isComptime() and !try sema.isComptimeKnown(uncoerced_arg)) {
   7133                     return sema.failWithNeededComptime(block, cai.argSrc(block, arg_index), null);
   7134                 }
   7135 
   7136                 if (sema.typeOf(uncoerced_arg).zigTypeTag(zcu) == .noreturn) {
   7137                     // This terminates resolution of arguments. The caller should
   7138                     // propagate this.
   7139                     return uncoerced_arg;
   7140                 }
   7141 
   7142                 if (sema.typeOf(uncoerced_arg).isError(zcu)) {
   7143                     zir_call.any_arg_is_error.* = true;
   7144                 }
   7145 
   7146                 break :arg_val uncoerced_arg;
   7147             },
   7148         };
   7149         const param_ty = maybe_param_ty orelse {
   7150             return sema.coerceVarArgParam(block, uncoerced_arg, cai.argSrc(block, arg_index));
   7151         };
   7152         switch (param_ty.toIntern()) {
   7153             .generic_poison_type => return uncoerced_arg,
   7154             else => return sema.coerceExtra(
   7155                 block,
   7156                 param_ty,
   7157                 uncoerced_arg,
   7158                 cai.argSrc(block, arg_index),
   7159                 .{ .param_src = .{
   7160                     .func_inst = func_inst,
   7161                     .param_i = @intCast(arg_index),
   7162                 } },
   7163             ) catch |err| switch (err) {
   7164                 error.NotCoercible => unreachable,
   7165                 else => |e| return e,
   7166             },
   7167         }
   7168     }
   7169 };
   7170 
   7171 fn analyzeCall(
   7172     sema: *Sema,
   7173     block: *Block,
   7174     callee: Air.Inst.Ref,
   7175     func_ty: Type,
   7176     func_src: LazySrcLoc,
   7177     call_src: LazySrcLoc,
   7178     modifier: std.builtin.CallModifier,
   7179     ensure_result_used: bool,
   7180     args_info: CallArgsInfo,
   7181     call_dbg_node: ?Zir.Inst.Index,
   7182     operation: CallOperation,
   7183 ) CompileError!Air.Inst.Ref {
   7184     const pt = sema.pt;
   7185     const zcu = pt.zcu;
   7186     const gpa = zcu.gpa;
   7187     const ip = &zcu.intern_pool;
   7188     const arena = sema.arena;
   7189 
   7190     const maybe_func_inst = try sema.funcDeclSrcInst(callee);
   7191     const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{
   7192         .base_node_inst = fn_decl_inst,
   7193         .offset = .{ .node_offset_fn_type_ret_ty = .zero },
   7194     } else func_src;
   7195 
   7196     const func_ty_info = zcu.typeToFunc(func_ty).?;
   7197     if (!callConvIsCallable(func_ty_info.cc)) {
   7198         return sema.failWithOwnedErrorMsg(block, msg: {
   7199             const msg = try sema.errMsg(
   7200                 func_src,
   7201                 "unable to call function with calling convention '{s}'",
   7202                 .{@tagName(func_ty_info.cc)},
   7203             );
   7204             errdefer msg.destroy(gpa);
   7205             if (maybe_func_inst) |func_inst| try sema.errNote(.{
   7206                 .base_node_inst = func_inst,
   7207                 .offset = .nodeOffset(.zero),
   7208             }, msg, "function declared here", .{});
   7209             break :msg msg;
   7210         });
   7211     }
   7212 
   7213     // We need this value in a few code paths.
   7214     const callee_val = try sema.resolveDefinedValue(block, call_src, callee);
   7215     // If the callee is a comptime-known *non-extern* function, `func_val` is populated.
   7216     // If it is a comptime-known extern function, `func_is_extern` is set instead.
   7217     // If it is not comptime-known, neither is set.
   7218     const func_val: ?Value, const func_is_extern: bool = if (callee_val) |c| switch (ip.indexToKey(c.toIntern())) {
   7219         .func => .{ c, false },
   7220         .ptr => switch (try sema.pointerDerefExtra(block, func_src, c)) {
   7221             .runtime_load, .needed_well_defined, .out_of_bounds => .{ null, false },
   7222             .val => |pointee| switch (ip.indexToKey(pointee.toIntern())) {
   7223                 .func => .{ pointee, false },
   7224                 .@"extern" => .{ null, true },
   7225                 else => unreachable,
   7226             },
   7227         },
   7228         .@"extern" => .{ null, true },
   7229         else => unreachable,
   7230     } else .{ null, false };
   7231 
   7232     if (func_ty_info.is_generic and func_val == null) {
   7233         return sema.failWithNeededComptime(block, func_src, .{ .simple = .generic_call_target });
   7234     }
   7235 
   7236     const inline_requested = func_ty_info.cc == .@"inline" or modifier == .always_inline;
   7237 
   7238     // If the modifier is `.compile_time`, or if the return type is non-generic and comptime-only,
   7239     // then we need to enter a comptime scope *now* to make sure the args are comptime-eval'd.
   7240     const old_block_comptime_reason = block.comptime_reason;
   7241     defer block.comptime_reason = old_block_comptime_reason;
   7242     if (!block.isComptime()) {
   7243         if (modifier == .compile_time) {
   7244             block.comptime_reason = .{ .reason = .{
   7245                 .src = call_src,
   7246                 .r = .{ .simple = .comptime_call_modifier },
   7247             } };
   7248         } else if (!inline_requested and try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) {
   7249             block.comptime_reason = .{
   7250                 .reason = .{
   7251                     .src = call_src,
   7252                     .r = .{
   7253                         .comptime_only_ret_ty = .{
   7254                             .ty = .fromInterned(func_ty_info.return_type),
   7255                             .is_generic_inst = false,
   7256                             .ret_ty_src = func_ret_ty_src,
   7257                         },
   7258                     },
   7259                 },
   7260             };
   7261         }
   7262     }
   7263 
   7264     // This is whether we already know this to be an inline call.
   7265     // If so, then comptime-known arguments are propagated when evaluating generic parameter/return types.
   7266     // We might still learn that this call is inline *after* evaluating the generic return type.
   7267     const early_known_inline = inline_requested or block.isComptime();
   7268 
   7269     // These values are undefined if `func_val == null`.
   7270     const fn_nav: InternPool.Nav, const fn_zir: Zir, const fn_tracked_inst: InternPool.TrackedInst.Index, const fn_zir_inst: Zir.Inst.Index, const fn_zir_info: Zir.FnInfo = if (func_val) |f| b: {
   7271         const info = ip.indexToKey(f.toIntern()).func;
   7272         const nav = ip.getNav(info.owner_nav);
   7273         const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail;
   7274         const file = zcu.fileByIndex(resolved_func_inst.file);
   7275         const zir_info = file.zir.?.getFnInfo(resolved_func_inst.inst);
   7276         break :b .{ nav, file.zir.?, info.zir_body_inst, resolved_func_inst.inst, zir_info };
   7277     } else .{ undefined, undefined, undefined, undefined, undefined };
   7278 
   7279     // This is the `inst_map` used when evaluating generic parameters and return types.
   7280     var generic_inst_map: InstMap = .{};
   7281     defer generic_inst_map.deinit(gpa);
   7282     if (func_ty_info.is_generic) {
   7283         try generic_inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body);
   7284     }
   7285 
   7286     // This exists so that `generic_block` below can include a "called from here" note back to this
   7287     // call site when analyzing generic parameter/return types.
   7288     var generic_inlining: Block.Inlining = if (func_ty_info.is_generic) .{
   7289         .call_block = block,
   7290         .call_src = call_src,
   7291         .func = func_val.?.toIntern(),
   7292         .is_generic_instantiation = true, // this allows the following fields to be `undefined`
   7293         .has_comptime_args = undefined,
   7294         .comptime_result = undefined,
   7295         .merges = undefined,
   7296     } else undefined;
   7297 
   7298     // This is the block in which we evaluate generic function components: that is, generic parameter
   7299     // types and the generic return type. This must not be used if the function is not generic.
   7300     // `comptime_reason` is set as needed.
   7301     var generic_block: Block = if (func_ty_info.is_generic) .{
   7302         .parent = null,
   7303         .sema = sema,
   7304         .namespace = fn_nav.analysis.?.namespace,
   7305         .instructions = .{},
   7306         .inlining = &generic_inlining,
   7307         .src_base_inst = fn_nav.analysis.?.zir_index,
   7308         .type_name_ctx = fn_nav.fqn,
   7309     } else undefined;
   7310     defer if (func_ty_info.is_generic) generic_block.instructions.deinit(gpa);
   7311 
   7312     if (func_ty_info.is_generic) {
   7313         // We certainly depend on the generic owner's signature!
   7314         try sema.declareDependency(.{ .src_hash = fn_tracked_inst });
   7315     }
   7316 
   7317     const args = try arena.alloc(Air.Inst.Ref, args_info.count());
   7318     for (args, 0..) |*arg, arg_idx| {
   7319         const param_ty: ?Type = if (arg_idx < func_ty_info.param_types.len) ty: {
   7320             const raw = func_ty_info.param_types.get(ip)[arg_idx];
   7321             if (raw != .generic_poison_type) break :ty .fromInterned(raw);
   7322 
   7323             // We must discover the generic parameter type.
   7324             assert(func_ty_info.is_generic);
   7325             const param_inst_idx = fn_zir_info.param_body[arg_idx];
   7326             const param_inst = fn_zir.instructions.get(@intFromEnum(param_inst_idx));
   7327             switch (param_inst.tag) {
   7328                 .param_anytype, .param_anytype_comptime => break :ty .generic_poison,
   7329                 .param, .param_comptime => {},
   7330                 else => unreachable,
   7331             }
   7332 
   7333             // Evaluate the generic parameter type. We need to switch out `sema.code` and `sema.inst_map`, because
   7334             // the function definition may be in a different file to the call site.
   7335             const old_code = sema.code;
   7336             const old_inst_map = sema.inst_map;
   7337             defer {
   7338                 generic_inst_map = sema.inst_map;
   7339                 sema.code = old_code;
   7340                 sema.inst_map = old_inst_map;
   7341             }
   7342             sema.code = fn_zir;
   7343             sema.inst_map = generic_inst_map;
   7344 
   7345             const extra = sema.code.extraData(Zir.Inst.Param, param_inst.data.pl_tok.payload_index);
   7346             const param_src = generic_block.tokenOffset(param_inst.data.pl_tok.src_tok);
   7347             const body = sema.code.bodySlice(extra.end, extra.data.type.body_len);
   7348 
   7349             generic_block.comptime_reason = .{ .reason = .{
   7350                 .r = .{ .simple = .function_parameters },
   7351                 .src = param_src,
   7352             } };
   7353 
   7354             const ty_ref = try sema.resolveInlineBody(&generic_block, body, param_inst_idx);
   7355             const param_ty = try sema.analyzeAsType(&generic_block, param_src, ty_ref);
   7356 
   7357             if (!param_ty.isValidParamType(zcu)) {
   7358                 const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
   7359                 return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{
   7360                     opaque_str, param_ty.fmt(pt),
   7361                 });
   7362             }
   7363 
   7364             break :ty param_ty;
   7365         } else null; // vararg
   7366 
   7367         arg.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, callee, maybe_func_inst);
   7368         const arg_ty = sema.typeOf(arg.*);
   7369         if (arg_ty.zigTypeTag(zcu) == .noreturn) {
   7370             return arg.*; // terminate analysis here
   7371         }
   7372 
   7373         if (func_ty_info.is_generic) {
   7374             // We need to put the argument into `generic_inst_map` so that other parameters can refer to it.
   7375             const param_inst_idx = fn_zir_info.param_body[arg_idx];
   7376             const declared_comptime = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsComptime(i) else false;
   7377             const param_is_comptime = declared_comptime or try arg_ty.comptimeOnlySema(pt);
   7378             // We allow comptime-known arguments to propagate to generic types not only for comptime
   7379             // parameters, but if the call is known to be inline.
   7380             if (param_is_comptime or early_known_inline) {
   7381                 if (param_is_comptime and !try sema.isComptimeKnown(arg.*)) {
   7382                     assert(!declared_comptime); // `analyzeArg` handles this
   7383                     const arg_src = args_info.argSrc(block, arg_idx);
   7384                     const param_ty_src: LazySrcLoc = .{
   7385                         .base_node_inst = maybe_func_inst.?, // the function is generic
   7386                         .offset = .{ .func_decl_param_ty = @intCast(arg_idx) },
   7387                     };
   7388                     return sema.failWithNeededComptime(
   7389                         block,
   7390                         arg_src,
   7391                         .{ .comptime_only_param_ty = .{ .ty = arg_ty, .param_ty_src = param_ty_src } },
   7392                     );
   7393                 }
   7394                 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, arg.*);
   7395             } else {
   7396                 // We need a dummy instruction with this type. It doesn't actually need to be in any block,
   7397                 // since it will never be referenced at runtime!
   7398                 const dummy: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   7399                 try sema.air_instructions.append(gpa, .{ .tag = .alloc, .data = .{ .ty = arg_ty } });
   7400                 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, dummy.toRef());
   7401             }
   7402         }
   7403     }
   7404 
   7405     // This return type is never generic poison.
   7406     // However, if it has an IES, it is always associated with the callee value.
   7407     // This is not correct for inline calls (where it should be an ad-hoc IES), nor for generic
   7408     // calls (where it should be the IES of the instantiation). However, it's how we print this
   7409     // in error messages.
   7410     const resolved_ret_ty: Type = ret_ty: {
   7411         if (!func_ty_info.is_generic) break :ret_ty .fromInterned(func_ty_info.return_type);
   7412 
   7413         const maybe_poison_bare = if (fn_zir_info.inferred_error_set) maybe_poison: {
   7414             break :maybe_poison ip.errorUnionPayload(func_ty_info.return_type);
   7415         } else func_ty_info.return_type;
   7416 
   7417         if (maybe_poison_bare != .generic_poison_type) break :ret_ty .fromInterned(func_ty_info.return_type);
   7418 
   7419         // Evaluate the generic return type. As with generic parameters, we switch out `sema.code` and `sema.inst_map`.
   7420 
   7421         assert(func_ty_info.is_generic);
   7422 
   7423         const old_code = sema.code;
   7424         const old_inst_map = sema.inst_map;
   7425         defer {
   7426             generic_inst_map = sema.inst_map;
   7427             sema.code = old_code;
   7428             sema.inst_map = old_inst_map;
   7429         }
   7430         sema.code = fn_zir;
   7431         sema.inst_map = generic_inst_map;
   7432 
   7433         generic_block.comptime_reason = .{ .reason = .{
   7434             .r = .{ .simple = .function_ret_ty },
   7435             .src = func_ret_ty_src,
   7436         } };
   7437 
   7438         const bare_ty = if (fn_zir_info.ret_ty_ref != .none) bare: {
   7439             assert(fn_zir_info.ret_ty_body.len == 0);
   7440             break :bare try sema.resolveType(&generic_block, func_ret_ty_src, fn_zir_info.ret_ty_ref);
   7441         } else bare: {
   7442             assert(fn_zir_info.ret_ty_body.len != 0);
   7443             const ty_ref = try sema.resolveInlineBody(&generic_block, fn_zir_info.ret_ty_body, fn_zir_inst);
   7444             break :bare try sema.analyzeAsType(&generic_block, func_ret_ty_src, ty_ref);
   7445         };
   7446         assert(bare_ty.toIntern() != .generic_poison_type);
   7447 
   7448         const full_ty = if (fn_zir_info.inferred_error_set) full: {
   7449             try sema.validateErrorUnionPayloadType(block, bare_ty, func_ret_ty_src);
   7450             const set = ip.errorUnionSet(func_ty_info.return_type);
   7451             break :full try pt.errorUnionType(.fromInterned(set), bare_ty);
   7452         } else bare_ty;
   7453 
   7454         if (!full_ty.isValidReturnType(zcu)) {
   7455             const opaque_str = if (full_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
   7456             return sema.fail(block, func_ret_ty_src, "{s}return type '{f}' not allowed", .{
   7457                 opaque_str, full_ty.fmt(pt),
   7458             });
   7459         }
   7460 
   7461         break :ret_ty full_ty;
   7462     };
   7463 
   7464     // If we've discovered after evaluating arguments that a generic function instantiation is
   7465     // comptime-only, then we can mark the block as comptime *now*.
   7466     if (!inline_requested and !block.isComptime() and try resolved_ret_ty.comptimeOnlySema(pt)) {
   7467         block.comptime_reason = .{
   7468             .reason = .{
   7469                 .src = call_src,
   7470                 .r = .{
   7471                     .comptime_only_ret_ty = .{
   7472                         .ty = resolved_ret_ty,
   7473                         .is_generic_inst = true,
   7474                         .ret_ty_src = func_ret_ty_src,
   7475                     },
   7476                 },
   7477             },
   7478         };
   7479     }
   7480 
   7481     if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
   7482 
   7483     const is_inline_call = block.isComptime() or inline_requested;
   7484 
   7485     if (!is_inline_call) {
   7486         if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: {
   7487             const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)});
   7488             errdefer msg.destroy(gpa);
   7489             switch (operation) {
   7490                 .call, .@"@call", .@"@panic", .@"error return" => {},
   7491                 .@"safety check" => try sema.errNote(call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}),
   7492             }
   7493             break :msg msg;
   7494         });
   7495         if (func_ty_info.cc == .auto) {
   7496             switch (sema.owner.unwrap()) {
   7497                 .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
   7498                 .func => |owner_func| ip.funcSetHasErrorTrace(owner_func, true),
   7499             }
   7500         }
   7501         for (args, 0..) |arg, arg_idx| {
   7502             try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg);
   7503         }
   7504         const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: {
   7505             if (!func_ty_info.is_generic) break :func .{ callee, args };
   7506 
   7507             // Instantiate the generic function!
   7508 
   7509             // This may be an overestimate, but it's definitely sufficient.
   7510             const max_runtime_args = args_info.count() - @popCount(func_ty_info.comptime_bits);
   7511             var runtime_args: std.ArrayListUnmanaged(Air.Inst.Ref) = try .initCapacity(arena, max_runtime_args);
   7512             var runtime_param_tys: std.ArrayListUnmanaged(InternPool.Index) = try .initCapacity(arena, max_runtime_args);
   7513 
   7514             const comptime_args = try arena.alloc(InternPool.Index, args_info.count());
   7515 
   7516             var noalias_bits: u32 = 0;
   7517 
   7518             for (args, comptime_args, 0..) |arg, *comptime_arg, arg_idx| {
   7519                 const arg_ty = sema.typeOf(arg);
   7520 
   7521                 const is_comptime = c: {
   7522                     if (std.math.cast(u5, arg_idx)) |i| {
   7523                         if (func_ty_info.paramIsComptime(i)) {
   7524                             break :c true;
   7525                         }
   7526                     }
   7527                     break :c try arg_ty.comptimeOnlySema(pt);
   7528                 };
   7529                 const is_noalias = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsNoalias(i) else false;
   7530 
   7531                 if (is_comptime) {
   7532                     // We already emitted an error if the argument isn't comptime-known.
   7533                     comptime_arg.* = (try sema.resolveValue(arg)).?.toIntern();
   7534                 } else {
   7535                     comptime_arg.* = .none;
   7536                     if (is_noalias) {
   7537                         const runtime_idx = runtime_args.items.len;
   7538                         noalias_bits |= @as(u32, 1) << @intCast(runtime_idx);
   7539                     }
   7540                     runtime_args.appendAssumeCapacity(arg);
   7541                     runtime_param_tys.appendAssumeCapacity(arg_ty.toIntern());
   7542                 }
   7543             }
   7544 
   7545             const bare_ret_ty = if (fn_zir_info.inferred_error_set) t: {
   7546                 break :t resolved_ret_ty.errorUnionPayload(zcu);
   7547             } else resolved_ret_ty;
   7548 
   7549             // We now need to actually create the function instance.
   7550             const func_instance = try ip.getFuncInstance(gpa, pt.tid, .{
   7551                 .param_types = runtime_param_tys.items,
   7552                 .noalias_bits = noalias_bits,
   7553                 .bare_return_type = bare_ret_ty.toIntern(),
   7554                 .is_noinline = func_ty_info.is_noinline,
   7555                 .inferred_error_set = fn_zir_info.inferred_error_set,
   7556                 .generic_owner = func_val.?.toIntern(),
   7557                 .comptime_args = comptime_args,
   7558             });
   7559             if (zcu.comp.debugIncremental()) {
   7560                 const nav = ip.indexToKey(func_instance).func.owner_nav;
   7561                 const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav);
   7562                 if (!gop.found_existing) gop.value_ptr.* = zcu.generation;
   7563             }
   7564 
   7565             // This call is problematic as it breaks guarantees about order-independency of semantic analysis.
   7566             // These guarantees are necessary for incremental compilation and parallel semantic analysis.
   7567             // See: #22410
   7568             zcu.funcInfo(func_instance).maxBranchQuota(ip, sema.branch_quota);
   7569 
   7570             break :func .{ Air.internedToRef(func_instance), runtime_args.items };
   7571         };
   7572 
   7573         ref_func: {
   7574             const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func;
   7575             if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func;
   7576             const orig_fn_index = ip.unwrapCoercedFunc(runtime_func_val.toIntern());
   7577             try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = orig_fn_index }));
   7578             try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
   7579         }
   7580 
   7581         const call_tag: Air.Inst.Tag = switch (modifier) {
   7582             .auto, .no_suspend => .call,
   7583             .never_tail => .call_never_tail,
   7584             .never_inline => .call_never_inline,
   7585             .always_tail => .call_always_tail,
   7586 
   7587             .always_inline,
   7588             .compile_time,
   7589             => unreachable,
   7590         };
   7591 
   7592         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.len);
   7593         const maybe_opv = try block.addInst(.{
   7594             .tag = call_tag,
   7595             .data = .{ .pl_op = .{
   7596                 .operand = runtime_func,
   7597                 .payload = sema.addExtraAssumeCapacity(Air.Call{
   7598                     .args_len = @intCast(runtime_args.len),
   7599                 }),
   7600             } },
   7601         });
   7602         sema.appendRefsAssumeCapacity(runtime_args);
   7603 
   7604         if (ensure_result_used) {
   7605             try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src);
   7606         }
   7607 
   7608         if (call_tag == .call_always_tail) {
   7609             const func_or_ptr_ty = sema.typeOf(runtime_func);
   7610             const runtime_func_ty = switch (func_or_ptr_ty.zigTypeTag(zcu)) {
   7611                 .@"fn" => func_or_ptr_ty,
   7612                 .pointer => func_or_ptr_ty.childType(zcu),
   7613                 else => unreachable,
   7614             };
   7615             return sema.handleTailCall(block, call_src, runtime_func_ty, maybe_opv);
   7616         }
   7617 
   7618         if (ip.isNoReturn(resolved_ret_ty.toIntern())) {
   7619             const want_check = c: {
   7620                 if (!block.wantSafety()) break :c false;
   7621                 if (func_val != null) break :c false;
   7622                 break :c true;
   7623             };
   7624             if (want_check) {
   7625                 try sema.safetyPanic(block, call_src, .noreturn_returned);
   7626             } else {
   7627                 _ = try block.addNoOp(.unreach);
   7628             }
   7629             return .unreachable_value;
   7630         }
   7631 
   7632         const result: Air.Inst.Ref = if (try sema.typeHasOnePossibleValue(sema.typeOf(maybe_opv))) |opv|
   7633             .fromValue(opv)
   7634         else
   7635             maybe_opv;
   7636 
   7637         return result;
   7638     }
   7639 
   7640     // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`.
   7641 
   7642     if (zcu.comp.time_report) |*tr| {
   7643         if (!block.isComptime()) {
   7644             tr.stats.n_inline_calls += 1;
   7645         }
   7646     }
   7647 
   7648     if (func_ty_info.is_noinline and !block.isComptime()) {
   7649         return sema.fail(block, call_src, "inline call of noinline function", .{});
   7650     }
   7651 
   7652     const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline";
   7653     if (modifier == .never_inline) {
   7654         const msg, const fail_block = msg: {
   7655             const msg = try sema.errMsg(call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type});
   7656             errdefer msg.destroy(gpa);
   7657             const fail_block = if (block.isComptime()) b: {
   7658                 break :b try block.explainWhyBlockIsComptime(msg);
   7659             } else block;
   7660             break :msg .{ msg, fail_block };
   7661         };
   7662         return sema.failWithOwnedErrorMsg(fail_block, msg);
   7663     }
   7664     if (func_ty_info.is_var_args) {
   7665         const msg, const fail_block = msg: {
   7666             const msg = try sema.errMsg(call_src, "{s} call of variadic function", .{call_type});
   7667             errdefer msg.destroy(gpa);
   7668             const fail_block = if (block.isComptime()) b: {
   7669                 break :b try block.explainWhyBlockIsComptime(msg);
   7670             } else block;
   7671             break :msg .{ msg, fail_block };
   7672         };
   7673         return sema.failWithOwnedErrorMsg(fail_block, msg);
   7674     }
   7675     if (func_val == null) {
   7676         if (func_is_extern) {
   7677             const msg, const fail_block = msg: {
   7678                 const msg = try sema.errMsg(call_src, "{s} call of extern function", .{call_type});
   7679                 errdefer msg.destroy(gpa);
   7680                 const fail_block = if (block.isComptime()) b: {
   7681                     break :b try block.explainWhyBlockIsComptime(msg);
   7682                 } else block;
   7683                 break :msg .{ msg, fail_block };
   7684             };
   7685             return sema.failWithOwnedErrorMsg(fail_block, msg);
   7686         }
   7687         return sema.failWithNeededComptime(
   7688             block,
   7689             func_src,
   7690             if (block.isComptime()) null else .{ .simple = .inline_call_target },
   7691         );
   7692     }
   7693 
   7694     if (block.isComptime()) {
   7695         for (args, 0..) |arg, arg_idx| {
   7696             if (!try sema.isComptimeKnown(arg)) {
   7697                 const arg_src = args_info.argSrc(block, arg_idx);
   7698                 return sema.failWithNeededComptime(block, arg_src, null);
   7699             }
   7700         }
   7701     }
   7702 
   7703     // For an inline call, we depend on the source code of the whole function definition.
   7704     try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index });
   7705 
   7706     try sema.emitBackwardBranch(block, call_src);
   7707 
   7708     const want_memoize = m: {
   7709         // TODO: comptime call memoization is currently not supported under incremental compilation
   7710         // since dependencies are not marked on callers. If we want to keep this around (we should
   7711         // check that it's worthwhile first!), each memoized call needs an `AnalUnit`.
   7712         if (zcu.comp.incremental) break :m false;
   7713         if (!block.isComptime()) break :m false;
   7714         for (args) |a| {
   7715             const val = (try sema.resolveValue(a)).?;
   7716             if (val.canMutateComptimeVarState(zcu)) break :m false;
   7717         }
   7718         break :m true;
   7719     };
   7720     const memoized_arg_values: []const InternPool.Index = if (want_memoize) arg_vals: {
   7721         const vals = try sema.arena.alloc(InternPool.Index, args.len);
   7722         for (vals, args) |*v, a| v.* = (try sema.resolveValue(a)).?.toIntern();
   7723         break :arg_vals vals;
   7724     } else undefined;
   7725     if (want_memoize) memoize: {
   7726         const memoized_call_index = ip.getIfExists(.{
   7727             .memoized_call = .{
   7728                 .func = func_val.?.toIntern(),
   7729                 .arg_values = memoized_arg_values,
   7730                 .result = undefined, // ignored by hash+eql
   7731                 .branch_count = undefined, // ignored by hash+eql
   7732             },
   7733         }) orelse break :memoize;
   7734         const memoized_call = ip.indexToKey(memoized_call_index).memoized_call;
   7735         if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) {
   7736             // Let the call play out se we get the correct source location for the
   7737             // "evaluation exceeded X backwards branches" error.
   7738             break :memoize;
   7739         }
   7740         sema.branch_count += memoized_call.branch_count;
   7741         const result = Air.internedToRef(memoized_call.result);
   7742         if (ensure_result_used) {
   7743             try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
   7744         }
   7745         return result;
   7746     }
   7747 
   7748     var new_ies: InferredErrorSet = .{ .func = .none };
   7749 
   7750     const old_inst_map = sema.inst_map;
   7751     const old_code = sema.code;
   7752     const old_func_index = sema.func_index;
   7753     const old_fn_ret_ty = sema.fn_ret_ty;
   7754     const old_fn_ret_ty_ies = sema.fn_ret_ty_ies;
   7755     const old_error_return_trace_index_on_fn_entry = sema.error_return_trace_index_on_fn_entry;
   7756     defer {
   7757         sema.inst_map.deinit(gpa);
   7758         sema.inst_map = old_inst_map;
   7759         sema.code = old_code;
   7760         sema.func_index = old_func_index;
   7761         sema.fn_ret_ty = old_fn_ret_ty;
   7762         sema.fn_ret_ty_ies = old_fn_ret_ty_ies;
   7763         sema.error_return_trace_index_on_fn_entry = old_error_return_trace_index_on_fn_entry;
   7764     }
   7765     sema.inst_map = .{};
   7766     sema.code = fn_zir;
   7767     sema.func_index = func_val.?.toIntern();
   7768     sema.fn_ret_ty = if (fn_zir_info.inferred_error_set) try pt.errorUnionType(
   7769         .fromInterned(.adhoc_inferred_error_set_type),
   7770         resolved_ret_ty.errorUnionPayload(zcu),
   7771     ) else resolved_ret_ty;
   7772     sema.fn_ret_ty_ies = if (fn_zir_info.inferred_error_set) &new_ies else null;
   7773 
   7774     try sema.inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body);
   7775     for (args, 0..) |arg, arg_idx| {
   7776         sema.inst_map.putAssumeCapacityNoClobber(fn_zir_info.param_body[arg_idx], arg);
   7777     }
   7778 
   7779     const need_debug_scope = !block.isComptime() and !block.is_typeof and !block.ownerModule().strip;
   7780     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   7781     try sema.air_instructions.append(gpa, .{
   7782         .tag = if (need_debug_scope) .dbg_inline_block else .block,
   7783         .data = undefined,
   7784     });
   7785 
   7786     var inlining: Block.Inlining = .{
   7787         .call_block = block,
   7788         .call_src = call_src,
   7789         .func = func_val.?.toIntern(),
   7790         .is_generic_instantiation = false,
   7791         .has_comptime_args = for (args) |a| {
   7792             if (try sema.isComptimeKnown(a)) break true;
   7793         } else false,
   7794         .comptime_result = undefined,
   7795         .merges = .{
   7796             .block_inst = block_inst,
   7797             .results = .empty,
   7798             .br_list = .empty,
   7799             .src_locs = .empty,
   7800         },
   7801     };
   7802     var child_block: Block = .{
   7803         .parent = null,
   7804         .sema = sema,
   7805         .namespace = fn_nav.analysis.?.namespace,
   7806         .instructions = .{},
   7807         .inlining = &inlining,
   7808         .is_typeof = block.is_typeof,
   7809         .comptime_reason = if (block.isComptime()) .inlining_parent else null,
   7810         .error_return_trace_index = block.error_return_trace_index,
   7811         .runtime_cond = block.runtime_cond,
   7812         .runtime_loop = block.runtime_loop,
   7813         .runtime_index = block.runtime_index,
   7814         .src_base_inst = fn_nav.analysis.?.zir_index,
   7815         .type_name_ctx = fn_nav.fqn,
   7816     };
   7817 
   7818     defer child_block.instructions.deinit(gpa);
   7819     defer inlining.merges.deinit(gpa);
   7820 
   7821     if (!inlining.has_comptime_args) {
   7822         var block_it = block;
   7823         while (block_it.inlining) |parent_inlining| {
   7824             if (!parent_inlining.is_generic_instantiation and
   7825                 !parent_inlining.has_comptime_args and
   7826                 parent_inlining.func == func_val.?.toIntern())
   7827             {
   7828                 return sema.fail(block, call_src, "inline call is recursive", .{});
   7829             }
   7830             block_it = parent_inlining.call_block;
   7831         }
   7832     }
   7833 
   7834     if (!block.isComptime() and !block.is_typeof) {
   7835         const zir_tags = sema.code.instructions.items(.tag);
   7836         const zir_datas = sema.code.instructions.items(.data);
   7837         for (fn_zir_info.param_body) |inst| switch (zir_tags[@intFromEnum(inst)]) {
   7838             .param, .param_comptime => {
   7839                 const extra = sema.code.extraData(Zir.Inst.Param, zir_datas[@intFromEnum(inst)].pl_tok.payload_index);
   7840                 const param_name = sema.code.nullTerminatedString(extra.data.name);
   7841                 const air_inst = sema.inst_map.get(inst).?;
   7842                 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name);
   7843             },
   7844             .param_anytype, .param_anytype_comptime => {
   7845                 const param_name = zir_datas[@intFromEnum(inst)].str_tok.get(sema.code);
   7846                 const air_inst = sema.inst_map.get(inst).?;
   7847                 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name);
   7848             },
   7849             else => {},
   7850         };
   7851     }
   7852 
   7853     child_block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block);
   7854     // Save the error trace as our first action in the function
   7855     // to match the behavior of runtime function calls.
   7856     const error_return_trace_index_on_parent_fn_entry = sema.error_return_trace_index_on_fn_entry;
   7857     sema.error_return_trace_index_on_fn_entry = child_block.error_return_trace_index;
   7858     defer sema.error_return_trace_index_on_fn_entry = error_return_trace_index_on_parent_fn_entry;
   7859 
   7860     // We temporarily set `allow_memoize` to `true` to track this comptime call.
   7861     // It is restored after the call finishes analysis, so that a caller may
   7862     // know whether an in-progress call (containing this call) may be memoized.
   7863     const old_allow_memoize = sema.allow_memoize;
   7864     defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize;
   7865     sema.allow_memoize = true;
   7866 
   7867     // Store the current eval branch count so we can find out how many eval branches
   7868     // the comptime call caused.
   7869     const old_branch_count = sema.branch_count;
   7870 
   7871     const result_raw: Air.Inst.Ref = result: {
   7872         sema.analyzeFnBody(&child_block, fn_zir_info.body) catch |err| switch (err) {
   7873             error.ComptimeReturn => break :result inlining.comptime_result,
   7874             else => |e| return e,
   7875         };
   7876         break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope);
   7877     };
   7878 
   7879     const maybe_opv: Air.Inst.Ref = if (try sema.resolveValue(result_raw)) |result_val| r: {
   7880         const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern());
   7881         break :r Air.internedToRef(val_resolved);
   7882     } else r: {
   7883         const resolved_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result_raw).toIntern());
   7884         if (resolved_ty == .none) break :r result_raw;
   7885         // TODO: mutate in place the previous instruction if possible
   7886         // rather than adding a bitcast instruction.
   7887         break :r try block.addBitCast(.fromInterned(resolved_ty), result_raw);
   7888     };
   7889 
   7890     if (block.isComptime()) {
   7891         const result_val = (try sema.resolveValue(maybe_opv)).?;
   7892         if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) {
   7893             _ = try pt.intern(.{ .memoized_call = .{
   7894                 .func = func_val.?.toIntern(),
   7895                 .arg_values = memoized_arg_values,
   7896                 .result = result_val.toIntern(),
   7897                 .branch_count = sema.branch_count - old_branch_count,
   7898             } });
   7899         }
   7900     }
   7901 
   7902     if (ensure_result_used) {
   7903         try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src);
   7904     }
   7905 
   7906     return maybe_opv;
   7907 }
   7908 
   7909 fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
   7910     const pt = sema.pt;
   7911     const zcu = pt.zcu;
   7912     const target = zcu.getTarget();
   7913     const backend = zcu.comp.getZigBackend();
   7914     if (!target_util.supportsTailCall(target, backend)) {
   7915         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", .{
   7916             @tagName(backend), @tagName(target.cpu.arch),
   7917         });
   7918     }
   7919     const owner_func_ty: Type = .fromInterned(zcu.funcInfo(sema.owner.unwrap().func).ty);
   7920     if (owner_func_ty.toIntern() != func_ty.toIntern()) {
   7921         return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{f}' does not match type of calling function '{f}'", .{
   7922             func_ty.fmt(pt), owner_func_ty.fmt(pt),
   7923         });
   7924     }
   7925     _ = try block.addUnOp(.ret, result);
   7926     return .unreachable_value;
   7927 }
   7928 
   7929 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7930     const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type;
   7931     const ty = try sema.pt.intType(int_type.signedness, int_type.bit_count);
   7932     return Air.internedToRef(ty.toIntern());
   7933 }
   7934 
   7935 fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7936     const tracy = trace(@src());
   7937     defer tracy.end();
   7938 
   7939     const pt = sema.pt;
   7940     const zcu = pt.zcu;
   7941     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   7942     const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
   7943     const child_type = try sema.resolveType(block, operand_src, inst_data.operand);
   7944     if (child_type.zigTypeTag(zcu) == .@"opaque") {
   7945         return sema.fail(block, operand_src, "opaque type '{f}' cannot be optional", .{child_type.fmt(pt)});
   7946     } else if (child_type.zigTypeTag(zcu) == .null) {
   7947         return sema.fail(block, operand_src, "type '{f}' cannot be optional", .{child_type.fmt(pt)});
   7948     }
   7949     const opt_type = try pt.optionalType(child_type.toIntern());
   7950 
   7951     return Air.internedToRef(opt_type.toIntern());
   7952 }
   7953 
   7954 fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7955     const pt = sema.pt;
   7956     const zcu = pt.zcu;
   7957     const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
   7958     const maybe_wrapped_indexable_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, bin.lhs) orelse return .generic_poison_type;
   7959     const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(zcu);
   7960     try indexable_ty.resolveFields(pt);
   7961     assert(indexable_ty.isIndexable(zcu)); // validated by a previous instruction
   7962     if (indexable_ty.zigTypeTag(zcu) == .@"struct") {
   7963         const elem_type = indexable_ty.fieldType(@intFromEnum(bin.rhs), zcu);
   7964         return Air.internedToRef(elem_type.toIntern());
   7965     } else {
   7966         const elem_type = indexable_ty.elemType2(zcu);
   7967         return Air.internedToRef(elem_type.toIntern());
   7968     }
   7969 }
   7970 
   7971 fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7972     const pt = sema.pt;
   7973     const zcu = pt.zcu;
   7974     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   7975     const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type;
   7976     const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu);
   7977     assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
   7978     const elem_ty = ptr_ty.childType(zcu);
   7979     if (elem_ty.toIntern() == .anyopaque_type) {
   7980         // The pointer's actual child type is effectively unknown, so it makes
   7981         // sense to represent it with a generic poison.
   7982         return .generic_poison_type;
   7983     }
   7984     return Air.internedToRef(ptr_ty.childType(zcu).toIntern());
   7985 }
   7986 
   7987 fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7988     const pt = sema.pt;
   7989     const zcu = pt.zcu;
   7990     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   7991     const src = block.nodeOffset(un_node.src_node);
   7992     const ptr_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type;
   7993     try sema.checkMemOperand(block, src, ptr_ty);
   7994     const elem_ty = switch (ptr_ty.ptrSize(zcu)) {
   7995         .slice, .many, .c => ptr_ty.childType(zcu),
   7996         .one => ptr_ty.childType(zcu).childType(zcu),
   7997     };
   7998     return Air.internedToRef(elem_ty.toIntern());
   7999 }
   8000 
   8001 fn zirSplatOpResultType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8002     const pt = sema.pt;
   8003     const zcu = pt.zcu;
   8004     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8005 
   8006     const raw_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type;
   8007     const vec_ty = raw_ty.optEuBaseType(zcu);
   8008 
   8009     switch (vec_ty.zigTypeTag(zcu)) {
   8010         .array, .vector => {},
   8011         else => return sema.fail(block, block.nodeOffset(un_node.src_node), "expected array or vector type, found '{f}'", .{vec_ty.fmt(pt)}),
   8012     }
   8013     return Air.internedToRef(vec_ty.childType(zcu).toIntern());
   8014 }
   8015 
   8016 fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8017     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8018     const len_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   8019     const elem_type_src = block.builtinCallArgSrc(inst_data.src_node, 1);
   8020     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8021     const len: u32 = @intCast(try sema.resolveInt(block, len_src, extra.lhs, .u32, .{ .simple = .vector_length }));
   8022     const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs);
   8023     try sema.checkVectorElemType(block, elem_type_src, elem_type);
   8024     const vector_type = try sema.pt.vectorType(.{
   8025         .len = len,
   8026         .child = elem_type.toIntern(),
   8027     });
   8028     return Air.internedToRef(vector_type.toIntern());
   8029 }
   8030 
   8031 fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8032     const tracy = trace(@src());
   8033     defer tracy.end();
   8034 
   8035     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8036     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8037     const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node });
   8038     const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node });
   8039     const len = try sema.resolveInt(block, len_src, extra.lhs, .usize, .{ .simple = .array_length });
   8040     const elem_type = try sema.resolveType(block, elem_src, extra.rhs);
   8041     try sema.validateArrayElemType(block, elem_type, elem_src);
   8042     const array_ty = try sema.pt.arrayType(.{
   8043         .len = len,
   8044         .child = elem_type.toIntern(),
   8045     });
   8046 
   8047     return Air.internedToRef(array_ty.toIntern());
   8048 }
   8049 
   8050 fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8051     const tracy = trace(@src());
   8052     defer tracy.end();
   8053 
   8054     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8055     const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
   8056     const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node });
   8057     const sentinel_src = block.src(.{ .node_offset_array_type_sentinel = inst_data.src_node });
   8058     const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node });
   8059     const len = try sema.resolveInt(block, len_src, extra.len, .usize, .{ .simple = .array_length });
   8060     const elem_type = try sema.resolveType(block, elem_src, extra.elem_type);
   8061     try sema.validateArrayElemType(block, elem_type, elem_src);
   8062     const uncasted_sentinel = try sema.resolveInst(extra.sentinel);
   8063     const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src);
   8064     const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{ .simple = .array_sentinel });
   8065     const array_ty = try sema.pt.arrayType(.{
   8066         .len = len,
   8067         .sentinel = sentinel_val.toIntern(),
   8068         .child = elem_type.toIntern(),
   8069     });
   8070     try sema.checkSentinelType(block, sentinel_src, elem_type);
   8071 
   8072     return Air.internedToRef(array_ty.toIntern());
   8073 }
   8074 
   8075 fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void {
   8076     const pt = sema.pt;
   8077     const zcu = pt.zcu;
   8078     if (elem_type.zigTypeTag(zcu) == .@"opaque") {
   8079         return sema.fail(block, elem_src, "array of opaque type '{f}' not allowed", .{elem_type.fmt(pt)});
   8080     } else if (elem_type.zigTypeTag(zcu) == .noreturn) {
   8081         return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{});
   8082     }
   8083 }
   8084 
   8085 fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8086     const tracy = trace(@src());
   8087     defer tracy.end();
   8088 
   8089     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8090     if (true) {
   8091         return sema.failWithUseOfAsync(block, block.nodeOffset(inst_data.src_node));
   8092     }
   8093     const zcu = sema.zcu;
   8094     const operand_src = block.src(.{ .node_offset_anyframe_type = inst_data.src_node });
   8095     const return_type = try sema.resolveType(block, operand_src, inst_data.operand);
   8096     const anyframe_type = try zcu.anyframeType(return_type);
   8097 
   8098     return Air.internedToRef(anyframe_type.toIntern());
   8099 }
   8100 
   8101 fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8102     const tracy = trace(@src());
   8103     defer tracy.end();
   8104 
   8105     const pt = sema.pt;
   8106     const zcu = pt.zcu;
   8107     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8108     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8109     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
   8110     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
   8111     const error_set = try sema.resolveType(block, lhs_src, extra.lhs);
   8112     const payload = try sema.resolveType(block, rhs_src, extra.rhs);
   8113 
   8114     if (error_set.zigTypeTag(zcu) != .error_set) {
   8115         return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{
   8116             error_set.fmt(pt),
   8117         });
   8118     }
   8119     try sema.validateErrorUnionPayloadType(block, payload, rhs_src);
   8120     const err_union_ty = try pt.errorUnionType(error_set, payload);
   8121     return Air.internedToRef(err_union_ty.toIntern());
   8122 }
   8123 
   8124 fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void {
   8125     const pt = sema.pt;
   8126     const zcu = pt.zcu;
   8127     if (payload_ty.zigTypeTag(zcu) == .@"opaque") {
   8128         return sema.fail(block, payload_src, "error union with payload of opaque type '{f}' not allowed", .{
   8129             payload_ty.fmt(pt),
   8130         });
   8131     } else if (payload_ty.zigTypeTag(zcu) == .error_set) {
   8132         return sema.fail(block, payload_src, "error union with payload of error set type '{f}' not allowed", .{
   8133             payload_ty.fmt(pt),
   8134         });
   8135     }
   8136 }
   8137 
   8138 fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8139     _ = block;
   8140     const pt = sema.pt;
   8141     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   8142     const name = try pt.zcu.intern_pool.getOrPutString(
   8143         sema.gpa,
   8144         pt.tid,
   8145         inst_data.get(sema.code),
   8146         .no_embedded_nulls,
   8147     );
   8148     _ = try pt.getErrorValue(name);
   8149     // Create an error set type with only this error value, and return the value.
   8150     const error_set_type = try pt.singleErrorSetType(name);
   8151     return Air.internedToRef((try pt.intern(.{ .err = .{
   8152         .ty = error_set_type.toIntern(),
   8153         .name = name,
   8154     } })));
   8155 }
   8156 
   8157 fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
   8158     const tracy = trace(@src());
   8159     defer tracy.end();
   8160 
   8161     const pt = sema.pt;
   8162     const zcu = pt.zcu;
   8163     const ip = &zcu.intern_pool;
   8164     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   8165     const src = block.nodeOffset(extra.node);
   8166     const operand_src = block.builtinCallArgSrc(extra.node, 0);
   8167     const uncasted_operand = try sema.resolveInst(extra.operand);
   8168     const operand = try sema.coerce(block, .anyerror, uncasted_operand, operand_src);
   8169     const err_int_ty = try pt.errorIntType();
   8170 
   8171     if (try sema.resolveValue(operand)) |val| {
   8172         if (val.isUndef(zcu)) {
   8173             return pt.undefRef(err_int_ty);
   8174         }
   8175         const err_name = ip.indexToKey(val.toIntern()).err.name;
   8176         return Air.internedToRef((try pt.intValue(
   8177             err_int_ty,
   8178             try pt.getErrorValue(err_name),
   8179         )).toIntern());
   8180     }
   8181 
   8182     const op_ty = sema.typeOf(uncasted_operand);
   8183     switch (try sema.resolveInferredErrorSetTy(block, src, op_ty.toIntern())) {
   8184         .anyerror_type => {},
   8185         else => |err_set_ty_index| {
   8186             const names = ip.indexToKey(err_set_ty_index).error_set_type.names;
   8187             switch (names.len) {
   8188                 0 => return Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()),
   8189                 1 => return pt.intRef(err_int_ty, ip.getErrorValueIfExists(names.get(ip)[0]).?),
   8190                 else => {},
   8191             }
   8192         },
   8193     }
   8194 
   8195     try sema.requireRuntimeBlock(block, src, operand_src);
   8196     return block.addBitCast(err_int_ty, operand);
   8197 }
   8198 
   8199 fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
   8200     const tracy = trace(@src());
   8201     defer tracy.end();
   8202 
   8203     const pt = sema.pt;
   8204     const zcu = pt.zcu;
   8205     const ip = &zcu.intern_pool;
   8206     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   8207     const src = block.nodeOffset(extra.node);
   8208     const operand_src = block.builtinCallArgSrc(extra.node, 0);
   8209     const uncasted_operand = try sema.resolveInst(extra.operand);
   8210     const err_int_ty = try pt.errorIntType();
   8211     const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src);
   8212 
   8213     if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
   8214         const int = try sema.usizeCast(block, operand_src, try value.toUnsignedIntSema(pt));
   8215         if (int > len: {
   8216             const mutate = &ip.global_error_set.mutate;
   8217             mutate.map.mutex.lock();
   8218             defer mutate.map.mutex.unlock();
   8219             break :len mutate.names.len;
   8220         } or int == 0)
   8221             return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int});
   8222         return Air.internedToRef((try pt.intern(.{ .err = .{
   8223             .ty = .anyerror_type,
   8224             .name = ip.global_error_set.shared.names.acquire().view().items(.@"0")[int - 1],
   8225         } })));
   8226     }
   8227     try sema.requireRuntimeBlock(block, src, operand_src);
   8228     if (block.wantSafety()) {
   8229         const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand);
   8230         const zero_val = Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern());
   8231         const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val);
   8232         const ok = try block.addBinOp(.bool_and, is_lt_len, is_non_zero);
   8233         try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
   8234     }
   8235     return block.addInst(.{
   8236         .tag = .bitcast,
   8237         .data = .{ .ty_op = .{
   8238             .ty = .anyerror_type,
   8239             .operand = operand,
   8240         } },
   8241     });
   8242 }
   8243 
   8244 fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8245     const tracy = trace(@src());
   8246     defer tracy.end();
   8247 
   8248     const pt = sema.pt;
   8249     const zcu = pt.zcu;
   8250     const ip = &zcu.intern_pool;
   8251     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8252     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8253     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
   8254     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
   8255     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
   8256     const lhs = try sema.resolveInst(extra.lhs);
   8257     const rhs = try sema.resolveInst(extra.rhs);
   8258     if (sema.typeOf(lhs).zigTypeTag(zcu) == .bool and sema.typeOf(rhs).zigTypeTag(zcu) == .bool) {
   8259         const msg = msg: {
   8260             const msg = try sema.errMsg(lhs_src, "expected error set type, found 'bool'", .{});
   8261             errdefer msg.destroy(sema.gpa);
   8262             try sema.errNote(src, msg, "'||' merges error sets; 'or' performs boolean OR", .{});
   8263             break :msg msg;
   8264         };
   8265         return sema.failWithOwnedErrorMsg(block, msg);
   8266     }
   8267     const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
   8268     const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);
   8269     if (lhs_ty.zigTypeTag(zcu) != .error_set)
   8270         return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{lhs_ty.fmt(pt)});
   8271     if (rhs_ty.zigTypeTag(zcu) != .error_set)
   8272         return sema.fail(block, rhs_src, "expected error set type, found '{f}'", .{rhs_ty.fmt(pt)});
   8273 
   8274     // Anything merged with anyerror is anyerror.
   8275     if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) {
   8276         return .anyerror_type;
   8277     }
   8278 
   8279     if (ip.isInferredErrorSetType(lhs_ty.toIntern())) {
   8280         switch (try sema.resolveInferredErrorSet(block, src, lhs_ty.toIntern())) {
   8281             // isAnyError might have changed from a false negative to a true
   8282             // positive after resolution.
   8283             .anyerror_type => return .anyerror_type,
   8284             else => {},
   8285         }
   8286     }
   8287     if (ip.isInferredErrorSetType(rhs_ty.toIntern())) {
   8288         switch (try sema.resolveInferredErrorSet(block, src, rhs_ty.toIntern())) {
   8289             // isAnyError might have changed from a false negative to a true
   8290             // positive after resolution.
   8291             .anyerror_type => return .anyerror_type,
   8292             else => {},
   8293         }
   8294     }
   8295 
   8296     const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty);
   8297     return Air.internedToRef(err_set_ty.toIntern());
   8298 }
   8299 
   8300 fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8301     _ = block;
   8302     const tracy = trace(@src());
   8303     defer tracy.end();
   8304 
   8305     const pt = sema.pt;
   8306     const zcu = pt.zcu;
   8307     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   8308     const name = inst_data.get(sema.code);
   8309     return Air.internedToRef((try pt.intern(.{
   8310         .enum_literal = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, name, .no_embedded_nulls),
   8311     })));
   8312 }
   8313 
   8314 fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: bool) CompileError!Air.Inst.Ref {
   8315     const tracy = trace(@src());
   8316     defer tracy.end();
   8317 
   8318     const pt = sema.pt;
   8319     const zcu = pt.zcu;
   8320     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8321     const src = block.nodeOffset(inst_data.src_node);
   8322     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   8323     const name = try zcu.intern_pool.getOrPutString(
   8324         sema.gpa,
   8325         pt.tid,
   8326         sema.code.nullTerminatedString(extra.field_name_start),
   8327         .no_embedded_nulls,
   8328     );
   8329 
   8330     const orig_ty: Type = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse .generic_poison;
   8331 
   8332     const uncoerced_result = res: {
   8333         if (orig_ty.toIntern() == .generic_poison_type) {
   8334             // Treat this as a normal enum literal.
   8335             break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
   8336         }
   8337 
   8338         var ty = orig_ty;
   8339         while (true) switch (ty.zigTypeTag(zcu)) {
   8340             .error_union => ty = ty.errorUnionPayload(zcu),
   8341             .optional => ty = ty.optionalChild(zcu),
   8342             .pointer => ty = if (ty.isSinglePointer(zcu)) ty.childType(zcu) else break,
   8343             .enum_literal, .error_set => {
   8344                 // Treat this as a normal enum literal.
   8345                 break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
   8346             },
   8347             else => break,
   8348         };
   8349 
   8350         break :res try sema.fieldVal(block, src, Air.internedToRef(ty.toIntern()), name, src);
   8351     };
   8352 
   8353     // Decl literals cannot lookup runtime `var`s.
   8354     if (!try sema.isComptimeKnown(uncoerced_result)) {
   8355         return sema.fail(block, src, "decl literal must be comptime-known", .{});
   8356     }
   8357 
   8358     if (do_coerce) {
   8359         return sema.coerce(block, orig_ty, uncoerced_result, src);
   8360     } else {
   8361         return uncoerced_result;
   8362     }
   8363 }
   8364 
   8365 fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8366     const pt = sema.pt;
   8367     const zcu = pt.zcu;
   8368     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8369     const src = block.nodeOffset(inst_data.src_node);
   8370     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   8371     const operand = try sema.resolveInst(inst_data.operand);
   8372     const operand_ty = sema.typeOf(operand);
   8373 
   8374     const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(zcu)) {
   8375         .@"enum" => operand,
   8376         .@"union" => blk: {
   8377             try operand_ty.resolveFields(pt);
   8378             const tag_ty = operand_ty.unionTagType(zcu) orelse {
   8379                 return sema.fail(
   8380                     block,
   8381                     operand_src,
   8382                     "untagged union '{f}' cannot be converted to integer",
   8383                     .{operand_ty.fmt(pt)},
   8384                 );
   8385             };
   8386 
   8387             break :blk try sema.unionToTag(block, tag_ty, operand, operand_src);
   8388         },
   8389         else => {
   8390             return sema.fail(block, operand_src, "expected enum or tagged union, found '{f}'", .{
   8391                 operand_ty.fmt(pt),
   8392             });
   8393         },
   8394     };
   8395     const enum_tag_ty = sema.typeOf(enum_tag);
   8396     const int_tag_ty = enum_tag_ty.intTagType(zcu);
   8397 
   8398     // TODO: use correct solution
   8399     // https://github.com/ziglang/zig/issues/15909
   8400     if (enum_tag_ty.enumFieldCount(zcu) == 0 and !enum_tag_ty.isNonexhaustiveEnum(zcu)) {
   8401         return sema.fail(block, operand_src, "cannot use @intFromEnum on empty enum '{f}'", .{
   8402             enum_tag_ty.fmt(pt),
   8403         });
   8404     }
   8405 
   8406     if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| {
   8407         return Air.internedToRef((try pt.getCoerced(opv, int_tag_ty)).toIntern());
   8408     }
   8409 
   8410     if (try sema.resolveValue(enum_tag)) |enum_tag_val| {
   8411         if (enum_tag_val.isUndef(zcu)) {
   8412             return pt.undefRef(int_tag_ty);
   8413         }
   8414 
   8415         const val = try enum_tag_val.intFromEnum(enum_tag_ty, pt);
   8416         return Air.internedToRef(val.toIntern());
   8417     }
   8418 
   8419     try sema.requireRuntimeBlock(block, src, operand_src);
   8420     return block.addBitCast(int_tag_ty, enum_tag);
   8421 }
   8422 
   8423 fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8424     const pt = sema.pt;
   8425     const zcu = pt.zcu;
   8426     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8427     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8428     const src = block.nodeOffset(inst_data.src_node);
   8429     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   8430     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt");
   8431     const operand = try sema.resolveInst(extra.rhs);
   8432     const operand_ty = sema.typeOf(operand);
   8433 
   8434     if (dest_ty.zigTypeTag(zcu) != .@"enum") {
   8435         return sema.fail(block, src, "expected enum, found '{f}'", .{dest_ty.fmt(pt)});
   8436     }
   8437     _ = try sema.checkIntType(block, operand_src, operand_ty);
   8438 
   8439     if (try sema.resolveValue(operand)) |int_val| {
   8440         if (dest_ty.isNonexhaustiveEnum(zcu)) {
   8441             const int_tag_ty = dest_ty.intTagType(zcu);
   8442             if (try sema.intFitsInType(int_val, int_tag_ty, null)) {
   8443                 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
   8444             }
   8445             return sema.fail(block, src, "int value '{f}' out of range of non-exhaustive enum '{f}'", .{
   8446                 int_val.fmtValueSema(pt, sema), dest_ty.fmt(pt),
   8447             });
   8448         }
   8449         if (int_val.isUndef(zcu)) {
   8450             return sema.failWithUseOfUndef(block, operand_src, null);
   8451         }
   8452         if (!(try sema.enumHasInt(dest_ty, int_val))) {
   8453             return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{
   8454                 dest_ty.fmt(pt), int_val.fmtValueSema(pt, sema),
   8455             });
   8456         }
   8457         return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
   8458     }
   8459 
   8460     if (dest_ty.intTagType(zcu).zigTypeTag(zcu) == .comptime_int) {
   8461         return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_enum });
   8462     }
   8463 
   8464     if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| {
   8465         if (block.wantSafety()) {
   8466             // The operand is runtime-known but the result is comptime-known. In
   8467             // this case we still need a safety check.
   8468             const expect_int_val = switch (zcu.intern_pool.indexToKey(opv.toIntern())) {
   8469                 .enum_tag => |enum_tag| enum_tag.int,
   8470                 else => unreachable,
   8471             };
   8472             const expect_int_coerced = try pt.getCoerced(.fromInterned(expect_int_val), operand_ty);
   8473             const ok = try block.addBinOp(.cmp_eq, operand, Air.internedToRef(expect_int_coerced.toIntern()));
   8474             try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
   8475         }
   8476         return Air.internedToRef(opv.toIntern());
   8477     }
   8478 
   8479     try sema.requireRuntimeBlock(block, src, operand_src);
   8480     if (block.wantSafety()) {
   8481         try sema.preparePanicId(src, .invalid_enum_value);
   8482         return block.addTyOp(.intcast_safe, dest_ty, operand);
   8483     }
   8484     return block.addTyOp(.intcast, dest_ty, operand);
   8485 }
   8486 
   8487 /// Pointer in, pointer out.
   8488 fn zirOptionalPayloadPtr(
   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 inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8498     const optional_ptr = try sema.resolveInst(inst_data.operand);
   8499     const src = block.nodeOffset(inst_data.src_node);
   8500 
   8501     return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false);
   8502 }
   8503 
   8504 fn analyzeOptionalPayloadPtr(
   8505     sema: *Sema,
   8506     block: *Block,
   8507     src: LazySrcLoc,
   8508     optional_ptr: Air.Inst.Ref,
   8509     safety_check: bool,
   8510     initializing: bool,
   8511 ) CompileError!Air.Inst.Ref {
   8512     const pt = sema.pt;
   8513     const zcu = pt.zcu;
   8514     const optional_ptr_ty = sema.typeOf(optional_ptr);
   8515     assert(optional_ptr_ty.zigTypeTag(zcu) == .pointer);
   8516 
   8517     const opt_type = optional_ptr_ty.childType(zcu);
   8518     if (opt_type.zigTypeTag(zcu) != .optional) {
   8519         return sema.failWithExpectedOptionalType(block, src, opt_type);
   8520     }
   8521 
   8522     const child_type = opt_type.optionalChild(zcu);
   8523     const child_pointer = try pt.ptrTypeSema(.{
   8524         .child = child_type.toIntern(),
   8525         .flags = .{
   8526             .is_const = optional_ptr_ty.isConstPtr(zcu),
   8527             .address_space = optional_ptr_ty.ptrAddressSpace(zcu),
   8528         },
   8529     });
   8530 
   8531     if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| {
   8532         if (initializing) {
   8533             if (sema.isComptimeMutablePtr(ptr_val)) {
   8534                 // Set the optional to non-null at comptime.
   8535                 // If the payload is OPV, we must use that value instead of undef.
   8536                 const payload_val = try sema.typeHasOnePossibleValue(child_type) orelse try pt.undefValue(child_type);
   8537                 const opt_val = try pt.intern(.{ .opt = .{
   8538                     .ty = opt_type.toIntern(),
   8539                     .val = payload_val.toIntern(),
   8540                 } });
   8541                 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(opt_val), opt_type);
   8542             } else {
   8543                 // Emit runtime instructions to set the optional non-null bit.
   8544                 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
   8545                 try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr);
   8546             }
   8547             return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern());
   8548         }
   8549         if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {
   8550             if (val.isNull(zcu)) {
   8551                 return sema.fail(block, src, "unable to unwrap null", .{});
   8552             }
   8553             return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern());
   8554         }
   8555     }
   8556 
   8557     try sema.requireRuntimeBlock(block, src, null);
   8558     if (safety_check and block.wantSafety()) {
   8559         const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
   8560         try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
   8561     }
   8562 
   8563     if (initializing) {
   8564         const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
   8565         try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr);
   8566         return opt_payload_ptr;
   8567     } else {
   8568         return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr);
   8569     }
   8570 }
   8571 
   8572 /// Value in, value out.
   8573 fn zirOptionalPayload(
   8574     sema: *Sema,
   8575     block: *Block,
   8576     inst: Zir.Inst.Index,
   8577     safety_check: bool,
   8578 ) CompileError!Air.Inst.Ref {
   8579     const tracy = trace(@src());
   8580     defer tracy.end();
   8581 
   8582     const pt = sema.pt;
   8583     const zcu = pt.zcu;
   8584     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8585     const src = block.nodeOffset(inst_data.src_node);
   8586     const operand = try sema.resolveInst(inst_data.operand);
   8587     const operand_ty = sema.typeOf(operand);
   8588     const result_ty = switch (operand_ty.zigTypeTag(zcu)) {
   8589         .optional => operand_ty.optionalChild(zcu),
   8590         .pointer => t: {
   8591             if (operand_ty.ptrSize(zcu) != .c) {
   8592                 return sema.failWithExpectedOptionalType(block, src, operand_ty);
   8593             }
   8594             // TODO https://github.com/ziglang/zig/issues/6597
   8595             if (true) break :t operand_ty;
   8596             const ptr_info = operand_ty.ptrInfo(zcu);
   8597             break :t try pt.ptrTypeSema(.{
   8598                 .child = ptr_info.child,
   8599                 .flags = .{
   8600                     .alignment = ptr_info.flags.alignment,
   8601                     .is_const = ptr_info.flags.is_const,
   8602                     .is_volatile = ptr_info.flags.is_volatile,
   8603                     .is_allowzero = ptr_info.flags.is_allowzero,
   8604                     .address_space = ptr_info.flags.address_space,
   8605                 },
   8606             });
   8607         },
   8608         else => return sema.failWithExpectedOptionalType(block, src, operand_ty),
   8609     };
   8610 
   8611     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
   8612         if (val.optionalValue(zcu)) |payload| return Air.internedToRef(payload.toIntern());
   8613         if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{});
   8614         if (safety_check and block.wantSafety()) {
   8615             try sema.safetyPanic(block, src, .unwrap_null);
   8616         } else {
   8617             _ = try block.addNoOp(.unreach);
   8618         }
   8619         return .unreachable_value;
   8620     }
   8621 
   8622     try sema.requireRuntimeBlock(block, src, null);
   8623     if (safety_check and block.wantSafety()) {
   8624         const is_non_null = try block.addUnOp(.is_non_null, operand);
   8625         try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
   8626     }
   8627     return block.addTyOp(.optional_payload, result_ty, operand);
   8628 }
   8629 
   8630 /// Value in, value out
   8631 fn zirErrUnionPayload(
   8632     sema: *Sema,
   8633     block: *Block,
   8634     inst: Zir.Inst.Index,
   8635 ) CompileError!Air.Inst.Ref {
   8636     const tracy = trace(@src());
   8637     defer tracy.end();
   8638 
   8639     const pt = sema.pt;
   8640     const zcu = pt.zcu;
   8641     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8642     const src = block.nodeOffset(inst_data.src_node);
   8643     const operand = try sema.resolveInst(inst_data.operand);
   8644     const operand_src = src;
   8645     const err_union_ty = sema.typeOf(operand);
   8646     if (err_union_ty.zigTypeTag(zcu) != .error_union) {
   8647         return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{
   8648             err_union_ty.fmt(pt),
   8649         });
   8650     }
   8651     return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false);
   8652 }
   8653 
   8654 fn analyzeErrUnionPayload(
   8655     sema: *Sema,
   8656     block: *Block,
   8657     src: LazySrcLoc,
   8658     err_union_ty: Type,
   8659     operand: Air.Inst.Ref,
   8660     operand_src: LazySrcLoc,
   8661     safety_check: bool,
   8662 ) CompileError!Air.Inst.Ref {
   8663     const pt = sema.pt;
   8664     const zcu = pt.zcu;
   8665     const payload_ty = err_union_ty.errorUnionPayload(zcu);
   8666     if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
   8667         if (val.getErrorName(zcu).unwrap()) |name| {
   8668             return sema.failWithComptimeErrorRetTrace(block, src, name);
   8669         }
   8670         return Air.internedToRef(zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.payload);
   8671     }
   8672 
   8673     try sema.requireRuntimeBlock(block, src, null);
   8674 
   8675     // If the error set has no fields then no safety check is needed.
   8676     if (safety_check and block.wantSafety() and
   8677         !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu))
   8678     {
   8679         try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
   8680     }
   8681 
   8682     if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_only_value| {
   8683         return Air.internedToRef(payload_only_value.toIntern());
   8684     }
   8685 
   8686     return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
   8687 }
   8688 
   8689 /// Pointer in, pointer out.
   8690 fn zirErrUnionPayloadPtr(
   8691     sema: *Sema,
   8692     block: *Block,
   8693     inst: Zir.Inst.Index,
   8694 ) CompileError!Air.Inst.Ref {
   8695     const tracy = trace(@src());
   8696     defer tracy.end();
   8697 
   8698     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8699     const operand = try sema.resolveInst(inst_data.operand);
   8700     const src = block.nodeOffset(inst_data.src_node);
   8701 
   8702     return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
   8703 }
   8704 
   8705 fn analyzeErrUnionPayloadPtr(
   8706     sema: *Sema,
   8707     block: *Block,
   8708     src: LazySrcLoc,
   8709     operand: Air.Inst.Ref,
   8710     safety_check: bool,
   8711     initializing: bool,
   8712 ) CompileError!Air.Inst.Ref {
   8713     const pt = sema.pt;
   8714     const zcu = pt.zcu;
   8715     const operand_ty = sema.typeOf(operand);
   8716     assert(operand_ty.zigTypeTag(zcu) == .pointer);
   8717 
   8718     if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) {
   8719         return sema.fail(block, src, "expected error union type, found '{f}'", .{
   8720             operand_ty.childType(zcu).fmt(pt),
   8721         });
   8722     }
   8723 
   8724     const err_union_ty = operand_ty.childType(zcu);
   8725     const payload_ty = err_union_ty.errorUnionPayload(zcu);
   8726     const operand_pointer_ty = try pt.ptrTypeSema(.{
   8727         .child = payload_ty.toIntern(),
   8728         .flags = .{
   8729             .is_const = operand_ty.isConstPtr(zcu),
   8730             .address_space = operand_ty.ptrAddressSpace(zcu),
   8731         },
   8732     });
   8733 
   8734     if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| {
   8735         if (initializing) {
   8736             if (sema.isComptimeMutablePtr(ptr_val)) {
   8737                 // Set the error union to non-error at comptime.
   8738                 // If the payload is OPV, we must use that value instead of undef.
   8739                 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
   8740                 const eu_val = try pt.intern(.{ .error_union = .{
   8741                     .ty = err_union_ty.toIntern(),
   8742                     .val = .{ .payload = payload_val.toIntern() },
   8743                 } });
   8744                 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(eu_val), err_union_ty);
   8745             } else {
   8746                 // Emit runtime instructions to set the error union error code.
   8747                 try sema.requireRuntimeBlock(block, src, null);
   8748                 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
   8749                 try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr);
   8750             }
   8751             return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern());
   8752         }
   8753         if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {
   8754             if (val.getErrorName(zcu).unwrap()) |name| {
   8755                 return sema.failWithComptimeErrorRetTrace(block, src, name);
   8756             }
   8757             return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern());
   8758         }
   8759     }
   8760 
   8761     try sema.requireRuntimeBlock(block, src, null);
   8762 
   8763     // If the error set has no fields then no safety check is needed.
   8764     if (safety_check and block.wantSafety() and
   8765         !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu))
   8766     {
   8767         try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
   8768     }
   8769 
   8770     if (initializing) {
   8771         const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
   8772         try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr);
   8773         return eu_payload_ptr;
   8774     } else {
   8775         return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand);
   8776     }
   8777 }
   8778 
   8779 /// Value in, value out
   8780 fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8781     const tracy = trace(@src());
   8782     defer tracy.end();
   8783 
   8784     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8785     const src = block.nodeOffset(inst_data.src_node);
   8786     const operand = try sema.resolveInst(inst_data.operand);
   8787     return sema.analyzeErrUnionCode(block, src, operand);
   8788 }
   8789 
   8790 /// If `operand` is comptime-known, asserts that it is an error value rather than a payload value.
   8791 fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   8792     const pt = sema.pt;
   8793     const zcu = pt.zcu;
   8794     const operand_ty = sema.typeOf(operand);
   8795     if (operand_ty.zigTypeTag(zcu) != .error_union) {
   8796         return sema.fail(block, src, "expected error union type, found '{f}'", .{
   8797             operand_ty.fmt(pt),
   8798         });
   8799     }
   8800 
   8801     const result_ty = operand_ty.errorUnionSet(zcu);
   8802 
   8803     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
   8804         return Air.internedToRef((try pt.intern(.{ .err = .{
   8805             .ty = result_ty.toIntern(),
   8806             .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
   8807         } })));
   8808     }
   8809 
   8810     try sema.requireRuntimeBlock(block, src, null);
   8811     return block.addTyOp(.unwrap_errunion_err, result_ty, operand);
   8812 }
   8813 
   8814 /// Pointer in, value out
   8815 fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8816     const tracy = trace(@src());
   8817     defer tracy.end();
   8818 
   8819     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8820     const src = block.nodeOffset(inst_data.src_node);
   8821     const operand = try sema.resolveInst(inst_data.operand);
   8822     return sema.analyzeErrUnionCodePtr(block, src, operand);
   8823 }
   8824 
   8825 fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   8826     const pt = sema.pt;
   8827     const zcu = pt.zcu;
   8828     const operand_ty = sema.typeOf(operand);
   8829     assert(operand_ty.zigTypeTag(zcu) == .pointer);
   8830 
   8831     if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) {
   8832         return sema.fail(block, src, "expected error union type, found '{f}'", .{
   8833             operand_ty.childType(zcu).fmt(pt),
   8834         });
   8835     }
   8836 
   8837     const result_ty = operand_ty.childType(zcu).errorUnionSet(zcu);
   8838 
   8839     if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
   8840         if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
   8841             assert(val.getErrorName(zcu) != .none);
   8842             return Air.internedToRef((try pt.intern(.{ .err = .{
   8843                 .ty = result_ty.toIntern(),
   8844                 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
   8845             } })));
   8846         }
   8847     }
   8848 
   8849     try sema.requireRuntimeBlock(block, src, null);
   8850     return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand);
   8851 }
   8852 
   8853 fn zirFunc(
   8854     sema: *Sema,
   8855     block: *Block,
   8856     inst: Zir.Inst.Index,
   8857     inferred_error_set: bool,
   8858 ) CompileError!Air.Inst.Ref {
   8859     const pt = sema.pt;
   8860     const zcu = pt.zcu;
   8861     const ip = &zcu.intern_pool;
   8862     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8863     const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
   8864     const target = zcu.getTarget();
   8865     const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node });
   8866     const src = block.nodeOffset(inst_data.src_node);
   8867 
   8868     var extra_index = extra.end;
   8869 
   8870     const ret_ty: Type = if (extra.data.ret_ty.is_generic)
   8871         .generic_poison
   8872     else switch (extra.data.ret_ty.body_len) {
   8873         0 => .void,
   8874         1 => blk: {
   8875             const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   8876             extra_index += 1;
   8877             break :blk try sema.resolveType(block, ret_ty_src, ret_ty_ref);
   8878         },
   8879         else => blk: {
   8880             const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_ty.body_len);
   8881             extra_index += ret_ty_body.len;
   8882 
   8883             const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, .type, .{ .simple = .function_ret_ty });
   8884             break :blk ret_ty_val.toType();
   8885         },
   8886     };
   8887 
   8888     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
   8889     const has_body = extra.data.body_len != 0;
   8890     if (has_body) {
   8891         extra_index += extra.data.body_len;
   8892         src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
   8893     }
   8894 
   8895     // If this instruction has a body, then it's a function declaration, and we decide
   8896     // the callconv based on whether it is exported. Otherwise, the callconv defaults
   8897     // to `.auto`.
   8898     const cc: std.builtin.CallingConvention = if (has_body) cc: {
   8899         const func_decl_nav = sema.owner.unwrap().nav_val;
   8900         const fn_is_exported = exported: {
   8901             const decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(ip) orelse return error.AnalysisFail;
   8902             const zir_decl = sema.code.getDeclaration(decl_inst);
   8903             break :exported zir_decl.linkage == .@"export";
   8904         };
   8905         if (fn_is_exported) {
   8906             break :cc target.cCallingConvention() orelse {
   8907                 // This target has no default C calling convention. We sometimes trigger a similar
   8908                 // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency,
   8909                 // let's eval that now and just get the transitive error. (It's guaranteed to error
   8910                 // because it does the exact `cCallingConvention` call we just did.)
   8911                 const cc_type = try sema.getBuiltinType(src, .CallingConvention);
   8912                 _ = try sema.namespaceLookupVal(
   8913                     block,
   8914                     LazySrcLoc.unneeded,
   8915                     cc_type.getNamespaceIndex(zcu),
   8916                     try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls),
   8917                 );
   8918                 // The above should have errored.
   8919                 @panic("std.builtin is corrupt");
   8920             };
   8921         } else {
   8922             break :cc .auto;
   8923         }
   8924     } else .auto;
   8925 
   8926     return sema.funcCommon(
   8927         block,
   8928         inst_data.src_node,
   8929         inst,
   8930         cc,
   8931         ret_ty,
   8932         false,
   8933         inferred_error_set,
   8934         has_body,
   8935         src_locs,
   8936         0,
   8937         false,
   8938     );
   8939 }
   8940 
   8941 fn resolveGenericBody(
   8942     sema: *Sema,
   8943     block: *Block,
   8944     src: LazySrcLoc,
   8945     body: []const Zir.Inst.Index,
   8946     func_inst: Zir.Inst.Index,
   8947     dest_ty: Type,
   8948     reason: ComptimeReason,
   8949 ) !Value {
   8950     assert(body.len != 0);
   8951 
   8952     // Make sure any nested param instructions don't clobber our work.
   8953     const prev_params = block.params;
   8954     block.params = .{};
   8955     defer {
   8956         block.params = prev_params;
   8957     }
   8958 
   8959     const uncasted = try sema.resolveInlineBody(block, body, func_inst);
   8960     const result = try sema.coerce(block, dest_ty, uncasted, src);
   8961     return sema.resolveConstDefinedValue(block, src, result, reason);
   8962 }
   8963 
   8964 /// Given a library name, examines if the library name should end up in
   8965 /// `link.File.Options.windows_libs` table (for example, libc is always
   8966 /// specified via dedicated flag `link_libc` instead),
   8967 /// and puts it there if it doesn't exist.
   8968 /// It also dupes the library name which can then be saved as part of the
   8969 /// respective `Decl` (either `ExternFn` or `Var`).
   8970 /// The liveness of the duped library name is tied to liveness of `Zcu`.
   8971 /// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`).
   8972 pub fn handleExternLibName(
   8973     sema: *Sema,
   8974     block: *Block,
   8975     src_loc: LazySrcLoc,
   8976     lib_name: []const u8,
   8977 ) CompileError!void {
   8978     blk: {
   8979         const pt = sema.pt;
   8980         const zcu = pt.zcu;
   8981         const comp = zcu.comp;
   8982         const target = zcu.getTarget();
   8983         log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
   8984         if (std.zig.target.isLibCLibName(target, lib_name)) {
   8985             if (!comp.config.link_libc) {
   8986                 return sema.fail(
   8987                     block,
   8988                     src_loc,
   8989                     "dependency on libc must be explicitly specified in the build command",
   8990                     .{},
   8991                 );
   8992             }
   8993             break :blk;
   8994         }
   8995         if (std.zig.target.isLibCxxLibName(target, lib_name)) {
   8996             if (!comp.config.link_libcpp) return sema.fail(
   8997                 block,
   8998                 src_loc,
   8999                 "dependency on libc++ must be explicitly specified in the build command",
   9000                 .{},
   9001             );
   9002             break :blk;
   9003         }
   9004         if (mem.eql(u8, lib_name, "unwind")) {
   9005             if (!comp.config.link_libunwind) return sema.fail(
   9006                 block,
   9007                 src_loc,
   9008                 "dependency on libunwind must be explicitly specified in the build command",
   9009                 .{},
   9010             );
   9011             break :blk;
   9012         }
   9013         if (!target.cpu.arch.isWasm() and !block.ownerModule().pic) {
   9014             return sema.fail(
   9015                 block,
   9016                 src_loc,
   9017                 "dependency on dynamic library '{s}' requires enabling Position Independent Code; fixed by '-l{s}' or '-fPIC'",
   9018                 .{ lib_name, lib_name },
   9019             );
   9020         }
   9021         comp.addLinkLib(lib_name) catch |err| {
   9022             return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{
   9023                 lib_name, @errorName(err),
   9024             });
   9025         };
   9026     }
   9027 }
   9028 
   9029 /// These are calling conventions that are confirmed to work with variadic functions.
   9030 /// Any calling conventions not included here are either not yet verified to work with variadic
   9031 /// functions or there are no more other calling conventions that support variadic functions.
   9032 const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention.Tag{
   9033     .x86_64_sysv,
   9034     .x86_64_win,
   9035     .x86_sysv,
   9036     .x86_win,
   9037     .aarch64_aapcs,
   9038     .aarch64_aapcs_darwin,
   9039     .aarch64_aapcs_win,
   9040     .aarch64_vfabi,
   9041     .aarch64_vfabi_sve,
   9042     .arm_aapcs,
   9043     .arm_aapcs_vfp,
   9044     .mips64_n64,
   9045     .mips64_n32,
   9046     .mips_o32,
   9047     .riscv64_lp64,
   9048     .riscv64_lp64_v,
   9049     .riscv32_ilp32,
   9050     .riscv32_ilp32_v,
   9051     .sparc64_sysv,
   9052     .sparc_sysv,
   9053     .powerpc64_elf,
   9054     .powerpc64_elf_altivec,
   9055     .powerpc64_elf_v2,
   9056     .powerpc_sysv,
   9057     .powerpc_sysv_altivec,
   9058     .powerpc_aix,
   9059     .powerpc_aix_altivec,
   9060     .wasm_mvp,
   9061     .arc_sysv,
   9062     .avr_gnu,
   9063     .bpf_std,
   9064     .csky_sysv,
   9065     .hexagon_sysv,
   9066     .hexagon_sysv_hvx,
   9067     .lanai_sysv,
   9068     .loongarch64_lp64,
   9069     .loongarch32_ilp32,
   9070     .m68k_sysv,
   9071     .m68k_gnu,
   9072     .m68k_rtd,
   9073     .msp430_eabi,
   9074     .s390x_sysv,
   9075     .s390x_sysv_vx,
   9076     .ve_sysv,
   9077     .xcore_xs1,
   9078     .xcore_xs2,
   9079     .xtensa_call0,
   9080     .xtensa_windowed,
   9081 };
   9082 fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention.Tag) bool {
   9083     return for (calling_conventions_supporting_var_args) |supported_cc| {
   9084         if (cc == supported_cc) return true;
   9085     } else false;
   9086 }
   9087 fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention.Tag) CompileError!void {
   9088     const CallingConventionsSupportingVarArgsList = struct {
   9089         arch: std.Target.Cpu.Arch,
   9090         pub fn format(ctx: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void {
   9091             var first = true;
   9092             for (calling_conventions_supporting_var_args) |cc_inner| {
   9093                 for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| {
   9094                     if (supported_arch == ctx.arch) break;
   9095                 } else continue; // callconv not supported by this arch
   9096                 if (!first) {
   9097                     try w.writeAll(", ");
   9098                 }
   9099                 first = false;
   9100                 try w.print("'{s}'", .{@tagName(cc_inner)});
   9101             }
   9102         }
   9103     };
   9104 
   9105     if (!callConvSupportsVarArgs(cc)) {
   9106         return sema.failWithOwnedErrorMsg(block, msg: {
   9107             const msg = try sema.errMsg(src, "variadic function does not support '{s}' calling convention", .{@tagName(cc)});
   9108             errdefer msg.destroy(sema.gpa);
   9109             const target = sema.pt.zcu.getTarget();
   9110             try sema.errNote(src, msg, "supported calling conventions: {f}", .{CallingConventionsSupportingVarArgsList{ .arch = target.cpu.arch }});
   9111             break :msg msg;
   9112         });
   9113     }
   9114 }
   9115 
   9116 fn callConvIsCallable(cc: std.builtin.CallingConvention.Tag) bool {
   9117     return switch (cc) {
   9118         .naked,
   9119 
   9120         .arm_interrupt,
   9121         .avr_interrupt,
   9122         .avr_signal,
   9123         .csky_interrupt,
   9124         .m68k_interrupt,
   9125         .mips_interrupt,
   9126         .mips64_interrupt,
   9127         .riscv32_interrupt,
   9128         .riscv64_interrupt,
   9129         .x86_interrupt,
   9130         .x86_64_interrupt,
   9131 
   9132         .amdgcn_kernel,
   9133         .nvptx_kernel,
   9134         .spirv_kernel,
   9135         .spirv_fragment,
   9136         .spirv_vertex,
   9137         => false,
   9138 
   9139         else => true,
   9140     };
   9141 }
   9142 
   9143 fn checkMergeAllowed(sema: *Sema, block: *Block, src: LazySrcLoc, peer_ty: Type) !void {
   9144     const pt = sema.pt;
   9145     const zcu = pt.zcu;
   9146     const target = zcu.getTarget();
   9147 
   9148     if (!peer_ty.isPtrAtRuntime(zcu)) {
   9149         return;
   9150     }
   9151 
   9152     const as = peer_ty.ptrAddressSpace(zcu);
   9153     if (!target_util.arePointersLogical(target, as)) {
   9154         return;
   9155     }
   9156 
   9157     return sema.failWithOwnedErrorMsg(block, msg: {
   9158         const msg = try sema.errMsg(src, "value with non-mergable pointer type '{f}' depends on runtime control flow", .{peer_ty.fmt(pt)});
   9159         errdefer msg.destroy(sema.gpa);
   9160 
   9161         const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
   9162         try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
   9163 
   9164         const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);
   9165         try sema.errNote(src, msg, "pointers with address space '{s}' cannot be returned from a branch on target {s}-{s} by compiler backend {s}", .{
   9166             @tagName(as),
   9167             @tagName(target.cpu.arch.family()),
   9168             @tagName(target.os.tag),
   9169             @tagName(backend),
   9170         });
   9171 
   9172         break :msg msg;
   9173     });
   9174 }
   9175 
   9176 const Section = union(enum) {
   9177     generic,
   9178     default,
   9179     explicit: InternPool.NullTerminatedString,
   9180 };
   9181 
   9182 fn funcCommon(
   9183     sema: *Sema,
   9184     block: *Block,
   9185     src_node_offset: std.zig.Ast.Node.Offset,
   9186     func_inst: Zir.Inst.Index,
   9187     cc: std.builtin.CallingConvention,
   9188     /// this might be Type.generic_poison
   9189     bare_return_type: Type,
   9190     var_args: bool,
   9191     inferred_error_set: bool,
   9192     has_body: bool,
   9193     src_locs: Zir.Inst.Func.SrcLocs,
   9194     noalias_bits: u32,
   9195     is_noinline: bool,
   9196 ) CompileError!Air.Inst.Ref {
   9197     const pt = sema.pt;
   9198     const zcu = pt.zcu;
   9199     const gpa = sema.gpa;
   9200     const target = zcu.getTarget();
   9201     const ip = &zcu.intern_pool;
   9202     const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset });
   9203     const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset });
   9204     const func_src = block.nodeOffset(src_node_offset);
   9205 
   9206     const ret_ty_requires_comptime = try bare_return_type.comptimeOnlySema(pt);
   9207     var is_generic = bare_return_type.isGenericPoison() or ret_ty_requires_comptime;
   9208 
   9209     var comptime_bits: u32 = 0;
   9210     for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| {
   9211         const param_ty: Type = .fromInterned(param_ty_ip);
   9212         const is_noalias = blk: {
   9213             const index = std.math.cast(u5, i) orelse break :blk false;
   9214             break :blk @as(u1, @truncate(noalias_bits >> index)) != 0;
   9215         };
   9216         const param_src = block.src(.{ .fn_proto_param = .{
   9217             .fn_proto_node_offset = src_node_offset,
   9218             .param_index = @intCast(i),
   9219         } });
   9220         const param_ty_comptime = try param_ty.comptimeOnlySema(pt);
   9221         const param_ty_generic = param_ty.isGenericPoison();
   9222         if (param_is_comptime or param_ty_comptime or param_ty_generic) {
   9223             is_generic = true;
   9224         }
   9225         if (param_is_comptime) {
   9226             comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error
   9227         }
   9228         if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(cc)) {
   9229             return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
   9230         }
   9231         if (param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc)) {
   9232             return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
   9233         }
   9234         if (!param_ty.isValidParamType(zcu)) {
   9235             const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
   9236             return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{
   9237                 opaque_str, param_ty.fmt(pt),
   9238             });
   9239         }
   9240         if (!param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc) and !try sema.validateExternType(param_ty, .param_ty)) {
   9241             const msg = msg: {
   9242                 const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{s}'", .{
   9243                     param_ty.fmt(pt), @tagName(cc),
   9244                 });
   9245                 errdefer msg.destroy(sema.gpa);
   9246 
   9247                 try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty);
   9248 
   9249                 try sema.addDeclaredHereNote(msg, param_ty);
   9250                 break :msg msg;
   9251             };
   9252             return sema.failWithOwnedErrorMsg(block, msg);
   9253         }
   9254         if (param_ty_comptime and !param_is_comptime and has_body and !block.isComptime()) {
   9255             const msg = msg: {
   9256                 const msg = try sema.errMsg(param_src, "parameter of type '{f}' must be declared comptime", .{
   9257                     param_ty.fmt(pt),
   9258                 });
   9259                 errdefer msg.destroy(sema.gpa);
   9260 
   9261                 try sema.explainWhyTypeIsComptime(msg, param_src, param_ty);
   9262 
   9263                 try sema.addDeclaredHereNote(msg, param_ty);
   9264                 break :msg msg;
   9265             };
   9266             return sema.failWithOwnedErrorMsg(block, msg);
   9267         }
   9268         if (!param_ty_generic and is_noalias and
   9269             !(param_ty.zigTypeTag(zcu) == .pointer or param_ty.isPtrLikeOptional(zcu)))
   9270         {
   9271             return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{});
   9272         }
   9273         switch (cc) {
   9274             .x86_64_interrupt, .x86_interrupt => {
   9275                 const err_code_size = target.ptrBitWidth();
   9276                 switch (i) {
   9277                     0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{s}' calling convention must be a pointer type", .{@tagName(cc)}),
   9278                     1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{s}' calling convention must be a {d}-bit integer", .{ @tagName(cc), err_code_size }),
   9279                     else => return sema.fail(block, param_src, "'{s}' calling convention supports up to 2 parameters, found {d}", .{ @tagName(cc), i + 1 }),
   9280                 }
   9281             },
   9282             .arm_interrupt,
   9283             .mips64_interrupt,
   9284             .mips_interrupt,
   9285             .riscv64_interrupt,
   9286             .riscv32_interrupt,
   9287             .avr_interrupt,
   9288             .csky_interrupt,
   9289             .m68k_interrupt,
   9290             .avr_signal,
   9291             => return sema.fail(block, param_src, "parameters are not allowed with '{s}' calling convention", .{@tagName(cc)}),
   9292             else => {},
   9293         }
   9294     }
   9295 
   9296     if (var_args) {
   9297         if (is_generic) {
   9298             return sema.fail(block, func_src, "generic function cannot be variadic", .{});
   9299         }
   9300         const va_args_src = block.src(.{
   9301             .fn_proto_param = .{
   9302                 .fn_proto_node_offset = src_node_offset,
   9303                 .param_index = @intCast(block.params.len), // va_arg must be the last parameter
   9304             },
   9305         });
   9306         try sema.checkCallConvSupportsVarArgs(block, va_args_src, cc);
   9307     }
   9308 
   9309     const ret_poison = bare_return_type.isGenericPoison();
   9310 
   9311     const param_types = block.params.items(.ty);
   9312 
   9313     if (inferred_error_set) {
   9314         assert(has_body);
   9315         if (!ret_poison)
   9316             try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src);
   9317         const func_index = try ip.getFuncDeclIes(gpa, pt.tid, .{
   9318             .owner_nav = sema.owner.unwrap().nav_val,
   9319 
   9320             .param_types = param_types,
   9321             .noalias_bits = noalias_bits,
   9322             .comptime_bits = comptime_bits,
   9323             .bare_return_type = bare_return_type.toIntern(),
   9324             .cc = cc,
   9325             .is_var_args = var_args,
   9326             .is_generic = is_generic,
   9327             .is_noinline = is_noinline,
   9328 
   9329             .zir_body_inst = try block.trackZir(func_inst),
   9330             .lbrace_line = src_locs.lbrace_line,
   9331             .rbrace_line = src_locs.rbrace_line,
   9332             .lbrace_column = @as(u16, @truncate(src_locs.columns)),
   9333             .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
   9334         });
   9335         return finishFunc(
   9336             sema,
   9337             block,
   9338             func_index,
   9339             .none,
   9340             ret_poison,
   9341             bare_return_type,
   9342             ret_ty_src,
   9343             cc,
   9344             ret_ty_requires_comptime,
   9345             func_inst,
   9346             cc_src,
   9347             is_noinline,
   9348         );
   9349     }
   9350 
   9351     const func_ty = try ip.getFuncType(gpa, pt.tid, .{
   9352         .param_types = param_types,
   9353         .noalias_bits = noalias_bits,
   9354         .comptime_bits = comptime_bits,
   9355         .return_type = bare_return_type.toIntern(),
   9356         .cc = cc,
   9357         .is_var_args = var_args,
   9358         .is_generic = is_generic,
   9359         .is_noinline = is_noinline,
   9360     });
   9361 
   9362     if (has_body) {
   9363         const func_index = try ip.getFuncDecl(gpa, pt.tid, .{
   9364             .owner_nav = sema.owner.unwrap().nav_val,
   9365             .ty = func_ty,
   9366             .cc = cc,
   9367             .is_noinline = is_noinline,
   9368             .zir_body_inst = try block.trackZir(func_inst),
   9369             .lbrace_line = src_locs.lbrace_line,
   9370             .rbrace_line = src_locs.rbrace_line,
   9371             .lbrace_column = @as(u16, @truncate(src_locs.columns)),
   9372             .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
   9373         });
   9374         return finishFunc(
   9375             sema,
   9376             block,
   9377             func_index,
   9378             func_ty,
   9379             ret_poison,
   9380             bare_return_type,
   9381             ret_ty_src,
   9382             cc,
   9383             ret_ty_requires_comptime,
   9384             func_inst,
   9385             cc_src,
   9386             is_noinline,
   9387         );
   9388     }
   9389 
   9390     return finishFunc(
   9391         sema,
   9392         block,
   9393         .none,
   9394         func_ty,
   9395         ret_poison,
   9396         bare_return_type,
   9397         ret_ty_src,
   9398         cc,
   9399         ret_ty_requires_comptime,
   9400         func_inst,
   9401         cc_src,
   9402         is_noinline,
   9403     );
   9404 }
   9405 
   9406 fn finishFunc(
   9407     sema: *Sema,
   9408     block: *Block,
   9409     opt_func_index: InternPool.Index,
   9410     func_ty: InternPool.Index,
   9411     ret_poison: bool,
   9412     bare_return_type: Type,
   9413     ret_ty_src: LazySrcLoc,
   9414     cc_resolved: std.builtin.CallingConvention,
   9415     ret_ty_requires_comptime: bool,
   9416     func_inst: Zir.Inst.Index,
   9417     cc_src: LazySrcLoc,
   9418     is_noinline: bool,
   9419 ) CompileError!Air.Inst.Ref {
   9420     const pt = sema.pt;
   9421     const zcu = pt.zcu;
   9422     const ip = &zcu.intern_pool;
   9423     const gpa = sema.gpa;
   9424 
   9425     const return_type: Type = if (opt_func_index == .none or ret_poison)
   9426         bare_return_type
   9427     else
   9428         .fromInterned(ip.funcTypeReturnType(ip.typeOf(opt_func_index)));
   9429 
   9430     if (!return_type.isValidReturnType(zcu)) {
   9431         const opaque_str = if (return_type.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
   9432         return sema.fail(block, ret_ty_src, "{s}return type '{f}' not allowed", .{
   9433             opaque_str, return_type.fmt(pt),
   9434         });
   9435     }
   9436     if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(cc_resolved) and
   9437         !try sema.validateExternType(return_type, .ret_ty))
   9438     {
   9439         const msg = msg: {
   9440             const msg = try sema.errMsg(ret_ty_src, "return type '{f}' not allowed in function with calling convention '{s}'", .{
   9441                 return_type.fmt(pt), @tagName(cc_resolved),
   9442             });
   9443             errdefer msg.destroy(gpa);
   9444 
   9445             try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, return_type, .ret_ty);
   9446 
   9447             try sema.addDeclaredHereNote(msg, return_type);
   9448             break :msg msg;
   9449         };
   9450         return sema.failWithOwnedErrorMsg(block, msg);
   9451     }
   9452 
   9453     // If the return type is comptime-only but not dependent on parameters then
   9454     // all parameter types also need to be comptime.
   9455     if (opt_func_index != .none and ret_ty_requires_comptime and !block.isComptime()) comptime_check: {
   9456         for (block.params.items(.is_comptime)) |is_comptime| {
   9457             if (!is_comptime) break;
   9458         } else break :comptime_check;
   9459 
   9460         const msg = try sema.errMsg(
   9461             ret_ty_src,
   9462             "function with comptime-only return type '{f}' requires all parameters to be comptime",
   9463             .{return_type.fmt(pt)},
   9464         );
   9465         errdefer msg.destroy(sema.gpa);
   9466         try sema.explainWhyTypeIsComptime(msg, ret_ty_src, return_type);
   9467 
   9468         const tags = sema.code.instructions.items(.tag);
   9469         const data = sema.code.instructions.items(.data);
   9470         const param_body = sema.code.getParamBody(func_inst);
   9471         for (
   9472             block.params.items(.is_comptime),
   9473             block.params.items(.name),
   9474             param_body[0..block.params.len],
   9475         ) |is_comptime, name_nts, param_index| {
   9476             if (!is_comptime) {
   9477                 const param_src = block.tokenOffset(switch (tags[@intFromEnum(param_index)]) {
   9478                     .param => data[@intFromEnum(param_index)].pl_tok.src_tok,
   9479                     .param_anytype => data[@intFromEnum(param_index)].str_tok.src_tok,
   9480                     else => unreachable,
   9481                 });
   9482                 const name = sema.code.nullTerminatedString(name_nts);
   9483                 if (name.len != 0) {
   9484                     try sema.errNote(param_src, msg, "param '{s}' is required to be comptime", .{name});
   9485                 } else {
   9486                     try sema.errNote(param_src, msg, "param is required to be comptime", .{});
   9487                 }
   9488             }
   9489         }
   9490         return sema.failWithOwnedErrorMsg(block, msg);
   9491     }
   9492 
   9493     validate_incoming_stack_align: {
   9494         const a: u64 = switch (cc_resolved) {
   9495             inline else => |payload| if (@TypeOf(payload) != void and @hasField(@TypeOf(payload), "incoming_stack_alignment"))
   9496                 payload.incoming_stack_alignment orelse break :validate_incoming_stack_align
   9497             else
   9498                 break :validate_incoming_stack_align,
   9499         };
   9500         if (!std.math.isPowerOfTwo(a)) {
   9501             return sema.fail(block, cc_src, "calling convention incoming stack alignment '{d}' is not a power of two", .{a});
   9502         }
   9503     }
   9504 
   9505     switch (cc_resolved) {
   9506         .x86_64_interrupt,
   9507         .x86_interrupt,
   9508         .arm_interrupt,
   9509         .mips64_interrupt,
   9510         .mips_interrupt,
   9511         .riscv64_interrupt,
   9512         .riscv32_interrupt,
   9513         .avr_interrupt,
   9514         .csky_interrupt,
   9515         .m68k_interrupt,
   9516         .avr_signal,
   9517         => if (return_type.zigTypeTag(zcu) != .void and return_type.zigTypeTag(zcu) != .noreturn) {
   9518             return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(cc_resolved)});
   9519         },
   9520         .@"inline" => if (is_noinline) {
   9521             return sema.fail(block, cc_src, "'noinline' function cannot have calling convention 'inline'", .{});
   9522         },
   9523         else => {},
   9524     }
   9525 
   9526     switch (zcu.callconvSupported(cc_resolved)) {
   9527         .ok => {},
   9528         .bad_arch => |allowed_archs| {
   9529             const ArchListFormatter = struct {
   9530                 archs: []const std.Target.Cpu.Arch,
   9531                 pub fn format(formatter: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void {
   9532                     for (formatter.archs, 0..) |arch, i| {
   9533                         if (i != 0)
   9534                             try w.writeAll(", ");
   9535                         try w.print("'{s}'", .{@tagName(arch)});
   9536                     }
   9537                 }
   9538             };
   9539             return sema.fail(block, cc_src, "calling convention '{s}' only available on architectures {f}", .{
   9540                 @tagName(cc_resolved),
   9541                 ArchListFormatter{ .archs = allowed_archs },
   9542             });
   9543         },
   9544         .bad_backend => |bad_backend| return sema.fail(block, cc_src, "calling convention '{s}' not supported by compiler backend '{s}'", .{
   9545             @tagName(cc_resolved),
   9546             @tagName(bad_backend),
   9547         }),
   9548     }
   9549 
   9550     return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty);
   9551 }
   9552 
   9553 fn zirParam(
   9554     sema: *Sema,
   9555     block: *Block,
   9556     inst: Zir.Inst.Index,
   9557     comptime_syntax: bool,
   9558 ) CompileError!void {
   9559     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
   9560     const src = block.tokenOffset(inst_data.src_tok);
   9561     const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
   9562     const param_name: Zir.NullTerminatedString = extra.data.name;
   9563     const body = sema.code.bodySlice(extra.end, extra.data.type.body_len);
   9564 
   9565     const param_ty: Type = if (extra.data.type.is_generic) .generic_poison else ty: {
   9566         // Make sure any nested param instructions don't clobber our work.
   9567         const prev_params = block.params;
   9568         block.params = .{};
   9569         defer {
   9570             block.params = prev_params;
   9571         }
   9572 
   9573         const param_ty_inst = try sema.resolveInlineBody(block, body, inst);
   9574         break :ty try sema.analyzeAsType(block, src, param_ty_inst);
   9575     };
   9576 
   9577     try block.params.append(sema.arena, .{
   9578         .ty = param_ty.toIntern(),
   9579         .is_comptime = comptime_syntax,
   9580         .name = param_name,
   9581     });
   9582 }
   9583 
   9584 fn zirParamAnytype(
   9585     sema: *Sema,
   9586     block: *Block,
   9587     inst: Zir.Inst.Index,
   9588     comptime_syntax: bool,
   9589 ) CompileError!void {
   9590     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   9591     const param_name: Zir.NullTerminatedString = inst_data.start;
   9592 
   9593     try block.params.append(sema.arena, .{
   9594         .ty = .generic_poison_type,
   9595         .is_comptime = comptime_syntax,
   9596         .name = param_name,
   9597     });
   9598 }
   9599 
   9600 fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9601     const tracy = trace(@src());
   9602     defer tracy.end();
   9603 
   9604     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9605     const src = block.nodeOffset(inst_data.src_node);
   9606     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
   9607     return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false);
   9608 }
   9609 
   9610 fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9611     const tracy = trace(@src());
   9612     defer tracy.end();
   9613 
   9614     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9615     const src = block.nodeOffset(inst_data.src_node);
   9616     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
   9617     return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true);
   9618 }
   9619 
   9620 fn analyzeAs(
   9621     sema: *Sema,
   9622     block: *Block,
   9623     src: LazySrcLoc,
   9624     zir_dest_type: Zir.Inst.Ref,
   9625     zir_operand: Zir.Inst.Ref,
   9626     no_cast_to_comptime_int: bool,
   9627 ) CompileError!Air.Inst.Ref {
   9628     const pt = sema.pt;
   9629     const zcu = pt.zcu;
   9630     const operand = try sema.resolveInst(zir_operand);
   9631     const dest_ty = try sema.resolveTypeOrPoison(block, src, zir_dest_type) orelse return operand;
   9632     switch (dest_ty.zigTypeTag(zcu)) {
   9633         .@"opaque" => return sema.fail(block, src, "cannot cast to opaque type '{f}'", .{dest_ty.fmt(pt)}),
   9634         .noreturn => return sema.fail(block, src, "cannot cast to noreturn", .{}),
   9635         else => {},
   9636     }
   9637 
   9638     const is_ret = if (zir_dest_type.toIndex()) |ptr_index|
   9639         sema.code.instructions.items(.tag)[@intFromEnum(ptr_index)] == .ret_type
   9640     else
   9641         false;
   9642     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) {
   9643         error.NotCoercible => unreachable,
   9644         else => |e| return e,
   9645     };
   9646 }
   9647 
   9648 fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9649     const tracy = trace(@src());
   9650     defer tracy.end();
   9651 
   9652     const pt = sema.pt;
   9653     const zcu = pt.zcu;
   9654     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   9655     const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   9656     const operand = try sema.resolveInst(inst_data.operand);
   9657     const operand_ty = sema.typeOf(operand);
   9658     const ptr_ty = operand_ty.scalarType(zcu);
   9659     const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
   9660     if (!ptr_ty.isPtrAtRuntime(zcu)) {
   9661         return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)});
   9662     }
   9663     const pointee_ty = ptr_ty.childType(zcu);
   9664     if (try ptr_ty.comptimeOnlySema(pt)) {
   9665         const msg = msg: {
   9666             const msg = try sema.errMsg(ptr_src, "comptime-only type '{f}' has no pointer address", .{pointee_ty.fmt(pt)});
   9667             errdefer msg.destroy(sema.gpa);
   9668             try sema.explainWhyTypeIsComptime(msg, ptr_src, pointee_ty);
   9669             break :msg msg;
   9670         };
   9671         return sema.failWithOwnedErrorMsg(block, msg);
   9672     }
   9673     const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;
   9674     const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .usize_type, .len = len }) else .usize;
   9675 
   9676     if (try sema.resolveValue(operand)) |operand_val| ct: {
   9677         if (!is_vector) {
   9678             if (operand_val.isUndef(zcu)) {
   9679                 return .undef_usize;
   9680             }
   9681             const addr = try operand_val.getUnsignedIntSema(pt) orelse {
   9682                 // Wasn't an integer pointer. This is a runtime operation.
   9683                 break :ct;
   9684             };
   9685             return Air.internedToRef((try pt.intValue(
   9686                 .usize,
   9687                 addr,
   9688             )).toIntern());
   9689         }
   9690         const new_elems = try sema.arena.alloc(InternPool.Index, len);
   9691         for (new_elems, 0..) |*new_elem, i| {
   9692             const ptr_val = try operand_val.elemValue(pt, i);
   9693             if (ptr_val.isUndef(zcu)) {
   9694                 new_elem.* = .undef_usize;
   9695                 continue;
   9696             }
   9697             const addr = try ptr_val.getUnsignedIntSema(pt) orelse {
   9698                 // A vector element wasn't an integer pointer. This is a runtime operation.
   9699                 break :ct;
   9700             };
   9701             new_elem.* = (try pt.intValue(
   9702                 .usize,
   9703                 addr,
   9704             )).toIntern();
   9705         }
   9706         return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern());
   9707     }
   9708     try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src);
   9709     try sema.validateRuntimeValue(block, ptr_src, operand);
   9710     try sema.checkLogicalPtrOperation(block, ptr_src, ptr_ty);
   9711     return block.addBitCast(dest_ty, operand);
   9712 }
   9713 
   9714 fn zirFieldPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9715     const tracy = trace(@src());
   9716     defer tracy.end();
   9717 
   9718     const pt = sema.pt;
   9719     const zcu = pt.zcu;
   9720     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9721     const src = block.nodeOffset(inst_data.src_node);
   9722     const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
   9723     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9724     const field_name = try zcu.intern_pool.getOrPutString(
   9725         sema.gpa,
   9726         pt.tid,
   9727         sema.code.nullTerminatedString(extra.field_name_start),
   9728         .no_embedded_nulls,
   9729     );
   9730     const object_ptr = try sema.resolveInst(extra.lhs);
   9731     return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src);
   9732 }
   9733 
   9734 fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9735     const tracy = trace(@src());
   9736     defer tracy.end();
   9737 
   9738     const pt = sema.pt;
   9739     const zcu = pt.zcu;
   9740     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9741     const src = block.nodeOffset(inst_data.src_node);
   9742     const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
   9743     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9744     const field_name = try zcu.intern_pool.getOrPutString(
   9745         sema.gpa,
   9746         pt.tid,
   9747         sema.code.nullTerminatedString(extra.field_name_start),
   9748         .no_embedded_nulls,
   9749     );
   9750     const object_ptr = try sema.resolveInst(extra.lhs);
   9751     return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
   9752 }
   9753 
   9754 fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9755     const tracy = trace(@src());
   9756     defer tracy.end();
   9757 
   9758     const pt = sema.pt;
   9759     const zcu = pt.zcu;
   9760     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9761     const src = block.nodeOffset(inst_data.src_node);
   9762     const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node });
   9763     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9764     const field_name = try zcu.intern_pool.getOrPutString(
   9765         sema.gpa,
   9766         pt.tid,
   9767         sema.code.nullTerminatedString(extra.field_name_start),
   9768         .no_embedded_nulls,
   9769     );
   9770     const object_ptr = try sema.resolveInst(extra.lhs);
   9771     const struct_ty = sema.typeOf(object_ptr).childType(zcu);
   9772     switch (struct_ty.zigTypeTag(zcu)) {
   9773         .@"struct", .@"union" => {
   9774             return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, true);
   9775         },
   9776         else => {
   9777             return sema.failWithStructInitNotSupported(block, src, struct_ty);
   9778         },
   9779     }
   9780 }
   9781 
   9782 fn zirFieldPtrNamedLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9783     const tracy = trace(@src());
   9784     defer tracy.end();
   9785 
   9786     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9787     const src = block.nodeOffset(inst_data.src_node);
   9788     const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
   9789     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
   9790     const object_ptr = try sema.resolveInst(extra.lhs);
   9791     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
   9792     return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src);
   9793 }
   9794 
   9795 fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9796     const tracy = trace(@src());
   9797     defer tracy.end();
   9798 
   9799     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9800     const src = block.nodeOffset(inst_data.src_node);
   9801     const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
   9802     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
   9803     const object_ptr = try sema.resolveInst(extra.lhs);
   9804     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
   9805     return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
   9806 }
   9807 
   9808 fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9809     const tracy = trace(@src());
   9810     defer tracy.end();
   9811 
   9812     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9813     const src = block.nodeOffset(inst_data.src_node);
   9814     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   9815     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   9816 
   9817     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast");
   9818     const operand = try sema.resolveInst(extra.rhs);
   9819 
   9820     return sema.intCast(block, block.nodeOffset(inst_data.src_node), dest_ty, src, operand, operand_src);
   9821 }
   9822 
   9823 fn intCast(
   9824     sema: *Sema,
   9825     block: *Block,
   9826     src: LazySrcLoc,
   9827     dest_ty: Type,
   9828     dest_ty_src: LazySrcLoc,
   9829     operand: Air.Inst.Ref,
   9830     operand_src: LazySrcLoc,
   9831 ) CompileError!Air.Inst.Ref {
   9832     const pt = sema.pt;
   9833     const zcu = pt.zcu;
   9834     const operand_ty = sema.typeOf(operand);
   9835     const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src);
   9836     const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
   9837 
   9838     if (try sema.isComptimeKnown(operand)) {
   9839         return sema.coerce(block, dest_ty, operand, operand_src);
   9840     } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
   9841         return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
   9842     }
   9843 
   9844     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src);
   9845     const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
   9846 
   9847     if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| {
   9848         // requirement: intCast(u0, input) iff input == 0
   9849         if (block.wantSafety()) {
   9850             try sema.requireRuntimeBlock(block, src, operand_src);
   9851             const wanted_info = dest_scalar_ty.intInfo(zcu);
   9852             const wanted_bits = wanted_info.bits;
   9853 
   9854             if (wanted_bits == 0) {
   9855                 const ok = if (is_vector) ok: {
   9856                     const zeros = try sema.splat(operand_ty, try pt.intValue(operand_scalar_ty, 0));
   9857                     const zero_inst = Air.internedToRef(zeros.toIntern());
   9858                     const is_in_range = try block.addCmpVector(operand, zero_inst, .eq);
   9859                     const all_in_range = try block.addReduce(is_in_range, .And);
   9860                     break :ok all_in_range;
   9861                 } else ok: {
   9862                     const zero_inst = Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern());
   9863                     const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst);
   9864                     break :ok is_in_range;
   9865                 };
   9866                 try sema.addSafetyCheck(block, src, ok, .integer_out_of_bounds);
   9867             }
   9868         }
   9869 
   9870         return Air.internedToRef(opv.toIntern());
   9871     }
   9872 
   9873     try sema.requireRuntimeBlock(block, src, operand_src);
   9874     if (block.wantSafety()) {
   9875         try sema.preparePanicId(src, .integer_out_of_bounds);
   9876         return block.addTyOp(.intcast_safe, dest_ty, operand);
   9877     }
   9878     return block.addTyOp(.intcast, dest_ty, operand);
   9879 }
   9880 
   9881 fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9882     const tracy = trace(@src());
   9883     defer tracy.end();
   9884 
   9885     const pt = sema.pt;
   9886     const zcu = pt.zcu;
   9887     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9888     const src = block.nodeOffset(inst_data.src_node);
   9889     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   9890     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   9891 
   9892     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast");
   9893     const operand = try sema.resolveInst(extra.rhs);
   9894     const operand_ty = sema.typeOf(operand);
   9895     switch (dest_ty.zigTypeTag(zcu)) {
   9896         .@"anyframe",
   9897         .comptime_float,
   9898         .comptime_int,
   9899         .enum_literal,
   9900         .error_set,
   9901         .error_union,
   9902         .@"fn",
   9903         .frame,
   9904         .noreturn,
   9905         .null,
   9906         .@"opaque",
   9907         .optional,
   9908         .type,
   9909         .undefined,
   9910         .void,
   9911         => return sema.fail(block, src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}),
   9912 
   9913         .@"enum" => {
   9914             const msg = msg: {
   9915                 const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)});
   9916                 errdefer msg.destroy(sema.gpa);
   9917                 switch (operand_ty.zigTypeTag(zcu)) {
   9918                     .int, .comptime_int => try sema.errNote(src, msg, "use @enumFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}),
   9919                     else => {},
   9920                 }
   9921 
   9922                 break :msg msg;
   9923             };
   9924             return sema.failWithOwnedErrorMsg(block, msg);
   9925         },
   9926 
   9927         .pointer => {
   9928             const msg = msg: {
   9929                 const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)});
   9930                 errdefer msg.destroy(sema.gpa);
   9931                 switch (operand_ty.zigTypeTag(zcu)) {
   9932                     .int, .comptime_int => try sema.errNote(src, msg, "use @ptrFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}),
   9933                     .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}),
   9934                     else => {},
   9935                 }
   9936 
   9937                 break :msg msg;
   9938             };
   9939             return sema.failWithOwnedErrorMsg(block, msg);
   9940         },
   9941         .@"struct", .@"union" => if (dest_ty.containerLayout(zcu) == .auto) {
   9942             const container = switch (dest_ty.zigTypeTag(zcu)) {
   9943                 .@"struct" => "struct",
   9944                 .@"union" => "union",
   9945                 else => unreachable,
   9946             };
   9947             return sema.fail(block, src, "cannot @bitCast to '{f}'; {s} does not have a guaranteed in-memory layout", .{
   9948                 dest_ty.fmt(pt), container,
   9949             });
   9950         },
   9951 
   9952         .array,
   9953         .bool,
   9954         .float,
   9955         .int,
   9956         .vector,
   9957         => {},
   9958     }
   9959     switch (operand_ty.zigTypeTag(zcu)) {
   9960         .@"anyframe",
   9961         .comptime_float,
   9962         .comptime_int,
   9963         .enum_literal,
   9964         .error_set,
   9965         .error_union,
   9966         .@"fn",
   9967         .frame,
   9968         .noreturn,
   9969         .null,
   9970         .@"opaque",
   9971         .optional,
   9972         .type,
   9973         .undefined,
   9974         .void,
   9975         => return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}),
   9976 
   9977         .@"enum" => {
   9978             const msg = msg: {
   9979                 const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});
   9980                 errdefer msg.destroy(sema.gpa);
   9981                 switch (dest_ty.zigTypeTag(zcu)) {
   9982                     .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromEnum to cast to '{f}'", .{dest_ty.fmt(pt)}),
   9983                     else => {},
   9984                 }
   9985 
   9986                 break :msg msg;
   9987             };
   9988             return sema.failWithOwnedErrorMsg(block, msg);
   9989         },
   9990         .pointer => {
   9991             const msg = msg: {
   9992                 const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});
   9993                 errdefer msg.destroy(sema.gpa);
   9994                 switch (dest_ty.zigTypeTag(zcu)) {
   9995                     .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromPtr to cast to '{f}'", .{dest_ty.fmt(pt)}),
   9996                     .pointer => try sema.errNote(operand_src, msg, "use @ptrCast to cast to '{f}'", .{dest_ty.fmt(pt)}),
   9997                     else => {},
   9998                 }
   9999 
  10000                 break :msg msg;
  10001             };
  10002             return sema.failWithOwnedErrorMsg(block, msg);
  10003         },
  10004         .@"struct", .@"union" => if (operand_ty.containerLayout(zcu) == .auto) {
  10005             const container = switch (operand_ty.zigTypeTag(zcu)) {
  10006                 .@"struct" => "struct",
  10007                 .@"union" => "union",
  10008                 else => unreachable,
  10009             };
  10010             return sema.fail(block, operand_src, "cannot @bitCast from '{f}'; {s} does not have a guaranteed in-memory layout", .{
  10011                 operand_ty.fmt(pt), container,
  10012             });
  10013         },
  10014 
  10015         .array,
  10016         .bool,
  10017         .float,
  10018         .int,
  10019         .vector,
  10020         => {},
  10021     }
  10022     return sema.bitCast(block, dest_ty, operand, block.nodeOffset(inst_data.src_node), operand_src);
  10023 }
  10024 
  10025 fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10026     const tracy = trace(@src());
  10027     defer tracy.end();
  10028 
  10029     const pt = sema.pt;
  10030     const zcu = pt.zcu;
  10031     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10032     const src = block.nodeOffset(inst_data.src_node);
  10033     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  10034     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10035 
  10036     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast");
  10037     const dest_scalar_ty = dest_ty.scalarType(zcu);
  10038 
  10039     const operand = try sema.resolveInst(extra.rhs);
  10040     const operand_ty = sema.typeOf(operand);
  10041     const operand_scalar_ty = operand_ty.scalarType(zcu);
  10042 
  10043     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
  10044     const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
  10045 
  10046     const target = zcu.getTarget();
  10047     const dest_is_comptime_float = switch (dest_scalar_ty.zigTypeTag(zcu)) {
  10048         .comptime_float => true,
  10049         .float => false,
  10050         else => return sema.fail(
  10051             block,
  10052             src,
  10053             "expected float or vector type, found '{f}'",
  10054             .{dest_ty.fmt(pt)},
  10055         ),
  10056     };
  10057 
  10058     switch (operand_scalar_ty.zigTypeTag(zcu)) {
  10059         .comptime_float, .float, .comptime_int => {},
  10060         else => return sema.fail(
  10061             block,
  10062             operand_src,
  10063             "expected float or vector type, found '{f}'",
  10064             .{operand_ty.fmt(pt)},
  10065         ),
  10066     }
  10067 
  10068     if (try sema.resolveValue(operand)) |operand_val| {
  10069         if (!is_vector) {
  10070             return Air.internedToRef((try operand_val.floatCast(dest_ty, pt)).toIntern());
  10071         }
  10072         const vec_len = operand_ty.vectorLen(zcu);
  10073         const new_elems = try sema.arena.alloc(InternPool.Index, vec_len);
  10074         for (new_elems, 0..) |*new_elem, i| {
  10075             const old_elem = try operand_val.elemValue(pt, i);
  10076             new_elem.* = (try old_elem.floatCast(dest_scalar_ty, pt)).toIntern();
  10077         }
  10078         return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern());
  10079     }
  10080     if (dest_is_comptime_float) {
  10081         return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{});
  10082     }
  10083     try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src);
  10084 
  10085     const src_bits = operand_scalar_ty.floatBits(target);
  10086     const dst_bits = dest_scalar_ty.floatBits(target);
  10087     if (dst_bits >= src_bits) {
  10088         return sema.coerce(block, dest_ty, operand, operand_src);
  10089     }
  10090     return block.addTyOp(.fptrunc, dest_ty, operand);
  10091 }
  10092 
  10093 fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10094     const tracy = trace(@src());
  10095     defer tracy.end();
  10096 
  10097     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10098     const src = block.nodeOffset(inst_data.src_node);
  10099     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10100     const array = try sema.resolveInst(extra.lhs);
  10101     const elem_index = try sema.resolveInst(extra.rhs);
  10102     return sema.elemVal(block, src, array, elem_index, src, false);
  10103 }
  10104 
  10105 fn zirElemPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10106     const tracy = trace(@src());
  10107     defer tracy.end();
  10108 
  10109     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10110     const src = block.nodeOffset(inst_data.src_node);
  10111     const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node });
  10112     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10113     const array_ptr = try sema.resolveInst(extra.lhs);
  10114     const uncoerced_elem_index = try sema.resolveInst(extra.rhs);
  10115     if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
  10116         const array_ptr_ty = sema.typeOf(array_ptr);
  10117         if (try sema.pointerDeref(block, src, array_ptr_val, array_ptr_ty)) |array_val| {
  10118             const array: Air.Inst.Ref = .fromValue(array_val);
  10119             return elemVal(sema, block, src, array, uncoerced_elem_index, elem_index_src, true);
  10120         }
  10121     }
  10122     const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src);
  10123     const elem_ptr = try elemPtr(sema, block, src, array_ptr, elem_index, elem_index_src, false, true);
  10124     return analyzeLoad(sema, block, src, elem_ptr, elem_index_src);
  10125 }
  10126 
  10127 fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10128     const tracy = trace(@src());
  10129     defer tracy.end();
  10130 
  10131     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm;
  10132     const array = try sema.resolveInst(inst_data.operand);
  10133     const elem_index = try sema.pt.intRef(.usize, inst_data.idx);
  10134     return sema.elemVal(block, LazySrcLoc.unneeded, array, elem_index, LazySrcLoc.unneeded, false);
  10135 }
  10136 
  10137 fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10138     const tracy = trace(@src());
  10139     defer tracy.end();
  10140 
  10141     const pt = sema.pt;
  10142     const zcu = pt.zcu;
  10143     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10144     const src = block.nodeOffset(inst_data.src_node);
  10145     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10146     const array_ptr = try sema.resolveInst(extra.lhs);
  10147     const elem_index = try sema.resolveInst(extra.rhs);
  10148     const indexable_ty = sema.typeOf(array_ptr);
  10149     if (indexable_ty.zigTypeTag(zcu) != .pointer) {
  10150         const capture_src = block.src(.{ .for_capture_from_input = inst_data.src_node });
  10151         const msg = msg: {
  10152             const msg = try sema.errMsg(capture_src, "pointer capture of non pointer type '{f}'", .{
  10153                 indexable_ty.fmt(pt),
  10154             });
  10155             errdefer msg.destroy(sema.gpa);
  10156             if (indexable_ty.isIndexable(zcu)) {
  10157                 try sema.errNote(src, msg, "consider using '&' here", .{});
  10158             }
  10159             break :msg msg;
  10160         };
  10161         return sema.failWithOwnedErrorMsg(block, msg);
  10162     }
  10163     return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false);
  10164 }
  10165 
  10166 fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10167     const tracy = trace(@src());
  10168     defer tracy.end();
  10169 
  10170     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10171     const src = block.nodeOffset(inst_data.src_node);
  10172     const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node });
  10173     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10174     const array_ptr = try sema.resolveInst(extra.lhs);
  10175     const uncoerced_elem_index = try sema.resolveInst(extra.rhs);
  10176     const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src);
  10177     return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true);
  10178 }
  10179 
  10180 fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10181     const tracy = trace(@src());
  10182     defer tracy.end();
  10183 
  10184     const pt = sema.pt;
  10185     const zcu = pt.zcu;
  10186     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10187     const src = block.nodeOffset(inst_data.src_node);
  10188     const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
  10189     const array_ptr = try sema.resolveInst(extra.ptr);
  10190     const elem_index = try pt.intRef(.usize, extra.index);
  10191     const array_ty = sema.typeOf(array_ptr).childType(zcu);
  10192     switch (array_ty.zigTypeTag(zcu)) {
  10193         .array, .vector => {},
  10194         else => if (!array_ty.isTuple(zcu)) {
  10195             return sema.failWithArrayInitNotSupported(block, src, array_ty);
  10196         },
  10197     }
  10198     return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true);
  10199 }
  10200 
  10201 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10202     const tracy = trace(@src());
  10203     defer tracy.end();
  10204 
  10205     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10206     const src = block.nodeOffset(inst_data.src_node);
  10207     const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
  10208     const array_ptr = try sema.resolveInst(extra.lhs);
  10209     const start = try sema.resolveInst(extra.start);
  10210     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10211     const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node });
  10212     const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
  10213 
  10214     return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false);
  10215 }
  10216 
  10217 fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10218     const tracy = trace(@src());
  10219     defer tracy.end();
  10220 
  10221     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10222     const src = block.nodeOffset(inst_data.src_node);
  10223     const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
  10224     const array_ptr = try sema.resolveInst(extra.lhs);
  10225     const start = try sema.resolveInst(extra.start);
  10226     const end = try sema.resolveInst(extra.end);
  10227     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10228     const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node });
  10229     const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
  10230 
  10231     return sema.analyzeSlice(block, src, array_ptr, start, end, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false);
  10232 }
  10233 
  10234 fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10235     const tracy = trace(@src());
  10236     defer tracy.end();
  10237 
  10238     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10239     const src = block.nodeOffset(inst_data.src_node);
  10240     const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
  10241     const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
  10242     const array_ptr = try sema.resolveInst(extra.lhs);
  10243     const start = try sema.resolveInst(extra.start);
  10244     const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end);
  10245     const sentinel = try sema.resolveInst(extra.sentinel);
  10246     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10247     const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node });
  10248     const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
  10249 
  10250     return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false);
  10251 }
  10252 
  10253 fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10254     const tracy = trace(@src());
  10255     defer tracy.end();
  10256 
  10257     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10258     const src = block.nodeOffset(inst_data.src_node);
  10259     const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
  10260     const array_ptr = try sema.resolveInst(extra.lhs);
  10261     const start = try sema.resolveInst(extra.start);
  10262     const len = try sema.resolveInst(extra.len);
  10263     const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel);
  10264     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10265     const start_src = block.src(.{ .node_offset_slice_start = extra.start_src_node_offset });
  10266     const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
  10267     const sentinel_src: LazySrcLoc = if (sentinel == .none)
  10268         LazySrcLoc.unneeded
  10269     else
  10270         block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
  10271 
  10272     return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
  10273 }
  10274 
  10275 fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10276     const tracy = trace(@src());
  10277     defer tracy.end();
  10278 
  10279     const pt = sema.pt;
  10280     const zcu = pt.zcu;
  10281 
  10282     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  10283 
  10284     const src = block.nodeOffset(inst_data.src_node);
  10285     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10286     const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
  10287 
  10288     // This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will
  10289     // have a double pointer if it was already a pointer.
  10290 
  10291     const lhs_ptr_ty = sema.typeOf(try sema.resolveInst(inst_data.operand));
  10292     const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) {
  10293         .pointer => lhs_ptr_ty.childType(zcu),
  10294         else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{lhs_ptr_ty.fmt(pt)}),
  10295     };
  10296 
  10297     const sentinel_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
  10298         .array => lhs_ty.childType(zcu),
  10299         .pointer => switch (lhs_ty.ptrSize(zcu)) {
  10300             .many, .c, .slice => lhs_ty.childType(zcu),
  10301             .one => s: {
  10302                 const lhs_elem_ty = lhs_ty.childType(zcu);
  10303                 break :s switch (lhs_elem_ty.zigTypeTag(zcu)) {
  10304                     .array => lhs_elem_ty.childType(zcu), // array element type
  10305                     else => return sema.fail(block, sentinel_src, "slice of single-item pointer cannot have sentinel", .{}),
  10306                 };
  10307             },
  10308         },
  10309         else => return sema.fail(block, src, "slice of non-array type '{f}'", .{lhs_ty.fmt(pt)}),
  10310     };
  10311 
  10312     return Air.internedToRef(sentinel_ty.toIntern());
  10313 }
  10314 
  10315 /// Holds common data used when analyzing or resolving switch prong bodies,
  10316 /// including setting up captures.
  10317 const SwitchProngAnalysis = struct {
  10318     sema: *Sema,
  10319     /// The block containing the `switch_block` itself.
  10320     parent_block: *Block,
  10321     operand: Operand,
  10322     /// If this switch is on an error set, this is the type to assign to the
  10323     /// `else` prong. If `null`, the prong should be unreachable.
  10324     else_error_ty: ?Type,
  10325     /// The index of the `switch_block` instruction itself.
  10326     switch_block_inst: Zir.Inst.Index,
  10327     /// The dummy index into which inline tag captures should be placed. May be
  10328     /// undefined if no prong has a tag capture.
  10329     tag_capture_inst: Zir.Inst.Index,
  10330 
  10331     const Operand = union(enum) {
  10332         /// This switch will be dispatched only once, with the given operand.
  10333         simple: struct {
  10334             /// The raw switch operand value. Always defined.
  10335             by_val: Air.Inst.Ref,
  10336             /// The switch operand *pointer*. Defined only if there is a prong
  10337             /// with a by-ref capture.
  10338             by_ref: Air.Inst.Ref,
  10339             /// The switch condition value. For unions, `operand` is the union
  10340             /// and `cond` is its enum tag value.
  10341             cond: Air.Inst.Ref,
  10342         },
  10343         /// This switch may be dispatched multiple times with `continue` syntax.
  10344         /// As such, the operand is stored in an alloc if needed.
  10345         loop: struct {
  10346             /// The `alloc` containing the `switch` operand for the active dispatch.
  10347             /// Each prong must load from this `alloc` to get captures.
  10348             /// If there are no captures, this may be undefined.
  10349             operand_alloc: Air.Inst.Ref,
  10350             /// Whether `operand_alloc` contains a by-val operand or a by-ref
  10351             /// operand.
  10352             operand_is_ref: bool,
  10353             /// The switch condition value for the *initial* dispatch. For
  10354             /// unions, this is the enum tag value.
  10355             init_cond: Air.Inst.Ref,
  10356         },
  10357     };
  10358 
  10359     /// Resolve a switch prong which is determined at comptime to have no peers.
  10360     /// Uses `resolveBlockBody`. Sets up captures as needed.
  10361     fn resolveProngComptime(
  10362         spa: SwitchProngAnalysis,
  10363         child_block: *Block,
  10364         prong_type: enum { normal, special },
  10365         prong_body: []const Zir.Inst.Index,
  10366         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10367         /// Must use the `switch_capture` field in `offset`.
  10368         capture_src: LazySrcLoc,
  10369         /// The set of all values which can reach this prong. May be undefined
  10370         /// if the prong is special or contains ranges.
  10371         case_vals: []const Air.Inst.Ref,
  10372         /// The inline capture of this prong. If this is not an inline prong,
  10373         /// this is `.none`.
  10374         inline_case_capture: Air.Inst.Ref,
  10375         /// Whether this prong has an inline tag capture. If `true`, then
  10376         /// `inline_case_capture` cannot be `.none`.
  10377         has_tag_capture: bool,
  10378         merges: *Block.Merges,
  10379     ) CompileError!Air.Inst.Ref {
  10380         const sema = spa.sema;
  10381         const src = spa.parent_block.nodeOffset(
  10382             sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src_node,
  10383         );
  10384 
  10385         // We can propagate `.cold` hints from this branch since it's comptime-known
  10386         // to be taken from the parent branch.
  10387         const parent_hint = sema.branch_hint;
  10388         defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
  10389 
  10390         if (has_tag_capture) {
  10391             const tag_ref = try spa.analyzeTagCapture(child_block, capture_src, inline_case_capture);
  10392             sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
  10393         }
  10394         defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
  10395 
  10396         switch (capture) {
  10397             .none => {
  10398                 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
  10399             },
  10400 
  10401             .by_val, .by_ref => {
  10402                 const capture_ref = try spa.analyzeCapture(
  10403                     child_block,
  10404                     capture == .by_ref,
  10405                     prong_type == .special,
  10406                     capture_src,
  10407                     case_vals,
  10408                     inline_case_capture,
  10409                 );
  10410 
  10411                 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) {
  10412                     // This prong should be unreachable!
  10413                     return .unreachable_value;
  10414                 }
  10415 
  10416                 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
  10417                 defer assert(sema.inst_map.remove(spa.switch_block_inst));
  10418 
  10419                 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
  10420             },
  10421         }
  10422     }
  10423 
  10424     /// Analyze a switch prong which may have peers at runtime.
  10425     /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed.
  10426     /// Returns the `BranchHint` for the prong.
  10427     fn analyzeProngRuntime(
  10428         spa: SwitchProngAnalysis,
  10429         case_block: *Block,
  10430         prong_type: enum { normal, special },
  10431         prong_body: []const Zir.Inst.Index,
  10432         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10433         /// Must use the `switch_capture` field in `offset`.
  10434         capture_src: LazySrcLoc,
  10435         /// The set of all values which can reach this prong. May be undefined
  10436         /// if the prong is special or contains ranges.
  10437         case_vals: []const Air.Inst.Ref,
  10438         /// The inline capture of this prong. If this is not an inline prong,
  10439         /// this is `.none`.
  10440         inline_case_capture: Air.Inst.Ref,
  10441         /// Whether this prong has an inline tag capture. If `true`, then
  10442         /// `inline_case_capture` cannot be `.none`.
  10443         has_tag_capture: bool,
  10444     ) CompileError!std.builtin.BranchHint {
  10445         const sema = spa.sema;
  10446 
  10447         if (has_tag_capture) {
  10448             const tag_ref = try spa.analyzeTagCapture(case_block, capture_src, inline_case_capture);
  10449             sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
  10450         }
  10451         defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
  10452 
  10453         switch (capture) {
  10454             .none => {
  10455                 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
  10456             },
  10457 
  10458             .by_val, .by_ref => {
  10459                 const capture_ref = try spa.analyzeCapture(
  10460                     case_block,
  10461                     capture == .by_ref,
  10462                     prong_type == .special,
  10463                     capture_src,
  10464                     case_vals,
  10465                     inline_case_capture,
  10466                 );
  10467 
  10468                 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) {
  10469                     // No need to analyze any further, the prong is unreachable
  10470                     return .none;
  10471                 }
  10472 
  10473                 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
  10474                 defer assert(sema.inst_map.remove(spa.switch_block_inst));
  10475 
  10476                 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
  10477             },
  10478         }
  10479     }
  10480 
  10481     fn analyzeTagCapture(
  10482         spa: SwitchProngAnalysis,
  10483         block: *Block,
  10484         capture_src: LazySrcLoc,
  10485         inline_case_capture: Air.Inst.Ref,
  10486     ) CompileError!Air.Inst.Ref {
  10487         const sema = spa.sema;
  10488         const pt = sema.pt;
  10489         const zcu = pt.zcu;
  10490         const operand_ty = switch (spa.operand) {
  10491             .simple => |s| sema.typeOf(s.by_val),
  10492             .loop => |l| ty: {
  10493                 const alloc_ty = sema.typeOf(l.operand_alloc);
  10494                 const alloc_child = alloc_ty.childType(zcu);
  10495                 if (l.operand_is_ref) break :ty alloc_child.childType(zcu);
  10496                 break :ty alloc_child;
  10497             },
  10498         };
  10499         if (operand_ty.zigTypeTag(zcu) != .@"union") {
  10500             const tag_capture_src: LazySrcLoc = .{
  10501                 .base_node_inst = capture_src.base_node_inst,
  10502                 .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture },
  10503             };
  10504             return sema.fail(block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{
  10505                 operand_ty.fmt(pt),
  10506             });
  10507         }
  10508         assert(inline_case_capture != .none);
  10509         return inline_case_capture;
  10510     }
  10511 
  10512     fn analyzeCapture(
  10513         spa: SwitchProngAnalysis,
  10514         block: *Block,
  10515         capture_byref: bool,
  10516         is_special_prong: bool,
  10517         capture_src: LazySrcLoc,
  10518         case_vals: []const Air.Inst.Ref,
  10519         inline_case_capture: Air.Inst.Ref,
  10520     ) CompileError!Air.Inst.Ref {
  10521         const sema = spa.sema;
  10522         const pt = sema.pt;
  10523         const zcu = pt.zcu;
  10524         const ip = &zcu.intern_pool;
  10525 
  10526         const zir_datas = sema.code.instructions.items(.data);
  10527         const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node;
  10528 
  10529         const operand_src = block.src(.{ .node_offset_switch_operand = switch_node_offset });
  10530 
  10531         const operand_val, const operand_ptr = switch (spa.operand) {
  10532             .simple => |s| .{ s.by_val, s.by_ref },
  10533             .loop => |l| op: {
  10534                 const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src);
  10535                 if (l.operand_is_ref) {
  10536                     const by_val = try sema.analyzeLoad(block, operand_src, loaded, operand_src);
  10537                     break :op .{ by_val, loaded };
  10538                 } else {
  10539                     break :op .{ loaded, undefined };
  10540                 }
  10541             },
  10542         };
  10543 
  10544         const operand_ty = sema.typeOf(operand_val);
  10545         const operand_ptr_ty = if (capture_byref) sema.typeOf(operand_ptr) else undefined;
  10546 
  10547         if (inline_case_capture != .none) {
  10548             const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inline_case_capture, undefined) catch unreachable;
  10549             if (operand_ty.zigTypeTag(zcu) == .@"union") {
  10550                 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?);
  10551                 const union_obj = zcu.typeToUnion(operand_ty).?;
  10552                 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  10553                 if (capture_byref) {
  10554                     const ptr_field_ty = try pt.ptrTypeSema(.{
  10555                         .child = field_ty.toIntern(),
  10556                         .flags = .{
  10557                             .is_const = !operand_ptr_ty.ptrIsMutable(zcu),
  10558                             .is_volatile = operand_ptr_ty.isVolatilePtr(zcu),
  10559                             .address_space = operand_ptr_ty.ptrAddressSpace(zcu),
  10560                         },
  10561                     });
  10562                     if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |union_ptr| {
  10563                         return Air.internedToRef((try union_ptr.ptrField(field_index, pt)).toIntern());
  10564                     }
  10565                     return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
  10566                 } else {
  10567                     if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |union_val| {
  10568                         const tag_and_val = ip.indexToKey(union_val.toIntern()).un;
  10569                         return Air.internedToRef(tag_and_val.val);
  10570                     }
  10571                     return block.addStructFieldVal(operand_val, field_index, field_ty);
  10572                 }
  10573             } else if (capture_byref) {
  10574                 return sema.uavRef(item_val.toIntern());
  10575             } else {
  10576                 return inline_case_capture;
  10577             }
  10578         }
  10579 
  10580         if (is_special_prong) {
  10581             if (capture_byref) {
  10582                 return operand_ptr;
  10583             }
  10584 
  10585             switch (operand_ty.zigTypeTag(zcu)) {
  10586                 .error_set => if (spa.else_error_ty) |ty| {
  10587                     return sema.bitCast(block, ty, operand_val, operand_src, null);
  10588                 } else {
  10589                     try sema.analyzeUnreachable(block, operand_src, false);
  10590                     return .unreachable_value;
  10591                 },
  10592                 else => return operand_val,
  10593             }
  10594         }
  10595 
  10596         switch (operand_ty.zigTypeTag(zcu)) {
  10597             .@"union" => {
  10598                 const union_obj = zcu.typeToUnion(operand_ty).?;
  10599                 const first_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable;
  10600 
  10601                 const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?;
  10602                 const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]);
  10603 
  10604                 const field_indices = try sema.arena.alloc(u32, case_vals.len);
  10605                 for (case_vals, field_indices) |item, *field_idx| {
  10606                     const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  10607                     field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?;
  10608                 }
  10609 
  10610                 // Fast path: if all the operands are the same type already, we don't need to hit
  10611                 // PTR! This will also allow us to emit simpler code.
  10612                 const same_types = for (field_indices[1..]) |field_idx| {
  10613                     const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10614                     if (!field_ty.eql(first_field_ty, zcu)) break false;
  10615                 } else true;
  10616 
  10617                 const capture_ty = if (same_types) first_field_ty else capture_ty: {
  10618                     // We need values to run PTR on, so make a bunch of undef constants.
  10619                     const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
  10620                     for (dummy_captures, field_indices) |*dummy, field_idx| {
  10621                         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10622                         dummy.* = try pt.undefRef(field_ty);
  10623                     }
  10624 
  10625                     const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
  10626                     for (case_srcs, 0..) |*case_src, i| {
  10627                         case_src.* = .{
  10628                             .base_node_inst = capture_src.base_node_inst,
  10629                             .offset = .{ .switch_case_item = .{
  10630                                 .switch_node_offset = switch_node_offset,
  10631                                 .case_idx = capture_src.offset.switch_capture.case_idx,
  10632                                 .item_idx = .{ .kind = .single, .index = @intCast(i) },
  10633                             } },
  10634                         };
  10635                     }
  10636 
  10637                     break :capture_ty sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
  10638                         error.AnalysisFail => {
  10639                             const msg = sema.err orelse return error.AnalysisFail;
  10640                             try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
  10641                             return error.AnalysisFail;
  10642                         },
  10643                         else => |e| return e,
  10644                     };
  10645                 };
  10646 
  10647                 // By-reference captures have some further restrictions which make them easier to emit
  10648                 if (capture_byref) {
  10649                     const operand_ptr_info = operand_ptr_ty.ptrInfo(zcu);
  10650                     const capture_ptr_ty = resolve: {
  10651                         // By-ref captures of hetereogeneous types are only allowed if all field
  10652                         // pointer types are peer resolvable to each other.
  10653                         // We need values to run PTR on, so make a bunch of undef constants.
  10654                         const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
  10655                         for (field_indices, dummy_captures) |field_idx, *dummy| {
  10656                             const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10657                             const field_ptr_ty = try pt.ptrTypeSema(.{
  10658                                 .child = field_ty.toIntern(),
  10659                                 .flags = .{
  10660                                     .is_const = operand_ptr_info.flags.is_const,
  10661                                     .is_volatile = operand_ptr_info.flags.is_volatile,
  10662                                     .address_space = operand_ptr_info.flags.address_space,
  10663                                     .alignment = union_obj.fieldAlign(ip, field_idx),
  10664                                 },
  10665                             });
  10666                             dummy.* = try pt.undefRef(field_ptr_ty);
  10667                         }
  10668                         const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
  10669                         for (case_srcs, 0..) |*case_src, i| {
  10670                             case_src.* = .{
  10671                                 .base_node_inst = capture_src.base_node_inst,
  10672                                 .offset = .{ .switch_case_item = .{
  10673                                     .switch_node_offset = switch_node_offset,
  10674                                     .case_idx = capture_src.offset.switch_capture.case_idx,
  10675                                     .item_idx = .{ .kind = .single, .index = @intCast(i) },
  10676                                 } },
  10677                             };
  10678                         }
  10679 
  10680                         break :resolve sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
  10681                             error.AnalysisFail => {
  10682                                 const msg = sema.err orelse return error.AnalysisFail;
  10683                                 try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{});
  10684                                 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
  10685                                 return error.AnalysisFail;
  10686                             },
  10687                             else => |e| return e,
  10688                         };
  10689                     };
  10690 
  10691                     if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| {
  10692                         if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty);
  10693                         const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt);
  10694                         return Air.internedToRef((try pt.getCoerced(field_ptr_val, capture_ptr_ty)).toIntern());
  10695                     }
  10696 
  10697                     try sema.requireRuntimeBlock(block, operand_src, null);
  10698                     return block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty);
  10699                 }
  10700 
  10701                 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |operand_val_val| {
  10702                     if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty);
  10703                     const union_val = ip.indexToKey(operand_val_val.toIntern()).un;
  10704                     if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty);
  10705                     const uncoerced = Air.internedToRef(union_val.val);
  10706                     return sema.coerce(block, capture_ty, uncoerced, operand_src);
  10707                 }
  10708 
  10709                 try sema.requireRuntimeBlock(block, operand_src, null);
  10710 
  10711                 if (same_types) {
  10712                     return block.addStructFieldVal(operand_val, first_field_index, capture_ty);
  10713                 }
  10714 
  10715                 // We may have to emit a switch block which coerces the operand to the capture type.
  10716                 // If we can, try to avoid that using in-memory coercions.
  10717                 const first_non_imc = in_mem: {
  10718                     for (field_indices, 0..) |field_idx, i| {
  10719                         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10720                         if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
  10721                             break :in_mem i;
  10722                         }
  10723                     }
  10724                     // All fields are in-memory coercible to the resolved type!
  10725                     // Just take the first field and bitcast the result.
  10726                     const uncoerced = try block.addStructFieldVal(operand_val, first_field_index, first_field_ty);
  10727                     return block.addBitCast(capture_ty, uncoerced);
  10728                 };
  10729 
  10730                 // By-val capture with heterogeneous types which are not all in-memory coercible to
  10731                 // the resolved capture type. We finally have to fall back to the ugly method.
  10732 
  10733                 // However, let's first track which operands are in-memory coercible. There may well
  10734                 // be several, and we can squash all of these cases into the same switch prong using
  10735                 // a simple bitcast. We'll make this the 'else' prong.
  10736 
  10737                 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len);
  10738                 in_mem_coercible.unset(first_non_imc);
  10739                 {
  10740                     const next = first_non_imc + 1;
  10741                     for (field_indices[next..], next..) |field_idx, i| {
  10742                         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10743                         if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
  10744                             in_mem_coercible.unset(i);
  10745                         }
  10746                     }
  10747                 }
  10748 
  10749                 const capture_block_inst = try block.addInstAsIndex(.{
  10750                     .tag = .block,
  10751                     .data = .{
  10752                         .ty_pl = .{
  10753                             .ty = Air.internedToRef(capture_ty.toIntern()),
  10754                             .payload = undefined, // updated below
  10755                         },
  10756                     },
  10757                 });
  10758 
  10759                 const prong_count = field_indices.len - in_mem_coercible.count();
  10760 
  10761                 const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints
  10762                 var cases_extra = try std.array_list.Managed(u32).initCapacity(sema.gpa, estimated_extra);
  10763                 defer cases_extra.deinit();
  10764 
  10765                 {
  10766                     // All branch hints are `.none`, so just add zero elems.
  10767                     comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0);
  10768                     const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable;
  10769                     try cases_extra.appendNTimes(0, need_elems);
  10770                 }
  10771 
  10772                 {
  10773                     // Non-bitcast cases
  10774                     var it = in_mem_coercible.iterator(.{ .kind = .unset });
  10775                     while (it.next()) |idx| {
  10776                         var coerce_block = block.makeSubBlock();
  10777                         defer coerce_block.instructions.deinit(sema.gpa);
  10778 
  10779                         const case_src: LazySrcLoc = .{
  10780                             .base_node_inst = capture_src.base_node_inst,
  10781                             .offset = .{ .switch_case_item = .{
  10782                                 .switch_node_offset = switch_node_offset,
  10783                                 .case_idx = capture_src.offset.switch_capture.case_idx,
  10784                                 .item_idx = .{ .kind = .single, .index = @intCast(idx) },
  10785                             } },
  10786                         };
  10787 
  10788                         const field_idx = field_indices[idx];
  10789                         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10790                         const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty);
  10791                         const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
  10792                         _ = try coerce_block.addBr(capture_block_inst, coerced);
  10793 
  10794                         try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  10795                             1 + // `item`, no ranges
  10796                             coerce_block.instructions.items.len);
  10797                         cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  10798                             .items_len = 1,
  10799                             .ranges_len = 0,
  10800                             .body_len = @intCast(coerce_block.instructions.items.len),
  10801                         }));
  10802                         cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item
  10803                         cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body
  10804                     }
  10805                 }
  10806                 const else_body_len = len: {
  10807                     // 'else' prong uses a bitcast
  10808                     var coerce_block = block.makeSubBlock();
  10809                     defer coerce_block.instructions.deinit(sema.gpa);
  10810 
  10811                     const first_imc_item_idx = in_mem_coercible.findFirstSet().?;
  10812                     const first_imc_field_idx = field_indices[first_imc_item_idx];
  10813                     const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]);
  10814                     const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty);
  10815                     const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
  10816                     _ = try coerce_block.addBr(capture_block_inst, coerced);
  10817 
  10818                     try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items));
  10819                     break :len coerce_block.instructions.items.len;
  10820                 };
  10821 
  10822                 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
  10823                     cases_extra.items.len +
  10824                     @typeInfo(Air.Block).@"struct".fields.len +
  10825                     1);
  10826 
  10827                 const switch_br_inst: u32 = @intCast(sema.air_instructions.len);
  10828                 try sema.air_instructions.append(sema.gpa, .{
  10829                     .tag = .switch_br,
  10830                     .data = .{
  10831                         .pl_op = .{
  10832                             .operand = undefined, // set by switch below
  10833                             .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
  10834                                 .cases_len = @intCast(prong_count),
  10835                                 .else_body_len = @intCast(else_body_len),
  10836                             }),
  10837                         },
  10838                     },
  10839                 });
  10840                 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
  10841 
  10842                 // Set up block body
  10843                 switch (spa.operand) {
  10844                     .simple => |s| {
  10845                         const air_datas = sema.air_instructions.items(.data);
  10846                         air_datas[switch_br_inst].pl_op.operand = s.cond;
  10847                         air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
  10848                             .body_len = 1,
  10849                         });
  10850                         sema.air_extra.appendAssumeCapacity(switch_br_inst);
  10851                     },
  10852                     .loop => {
  10853                         // The block must first extract the tag from the loaded union.
  10854                         const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  10855                         try sema.air_instructions.append(sema.gpa, .{
  10856                             .tag = .get_union_tag,
  10857                             .data = .{ .ty_op = .{
  10858                                 .ty = Air.internedToRef(union_obj.enum_tag_ty),
  10859                                 .operand = operand_val,
  10860                             } },
  10861                         });
  10862                         const air_datas = sema.air_instructions.items(.data);
  10863                         air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef();
  10864                         air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
  10865                             .body_len = 2,
  10866                         });
  10867                         sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst));
  10868                         sema.air_extra.appendAssumeCapacity(switch_br_inst);
  10869                     },
  10870                 }
  10871 
  10872                 return capture_block_inst.toRef();
  10873             },
  10874             .error_set => {
  10875                 if (capture_byref) {
  10876                     return sema.fail(
  10877                         block,
  10878                         capture_src,
  10879                         "error set cannot be captured by reference",
  10880                         .{},
  10881                     );
  10882                 }
  10883 
  10884                 if (case_vals.len == 1) {
  10885                     const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable;
  10886                     const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);
  10887                     return sema.bitCast(block, item_ty, operand_val, operand_src, null);
  10888                 }
  10889 
  10890                 var names: InferredErrorSet.NameMap = .{};
  10891                 try names.ensureUnusedCapacity(sema.arena, case_vals.len);
  10892                 for (case_vals) |err| {
  10893                     const err_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, err, undefined) catch unreachable;
  10894                     names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {});
  10895                 }
  10896                 const error_ty = try pt.errorSetFromUnsortedNames(names.keys());
  10897                 return sema.bitCast(block, error_ty, operand_val, operand_src, null);
  10898             },
  10899             else => {
  10900                 // In this case the capture value is just the passed-through value
  10901                 // of the switch condition.
  10902                 if (capture_byref) {
  10903                     return operand_ptr;
  10904                 } else {
  10905                     return operand_val;
  10906                 }
  10907             },
  10908         }
  10909     }
  10910 };
  10911 
  10912 fn switchCond(
  10913     sema: *Sema,
  10914     block: *Block,
  10915     src: LazySrcLoc,
  10916     operand: Air.Inst.Ref,
  10917 ) CompileError!Air.Inst.Ref {
  10918     const pt = sema.pt;
  10919     const zcu = pt.zcu;
  10920     const operand_ty = sema.typeOf(operand);
  10921     switch (operand_ty.zigTypeTag(zcu)) {
  10922         .type,
  10923         .void,
  10924         .bool,
  10925         .int,
  10926         .float,
  10927         .comptime_float,
  10928         .comptime_int,
  10929         .enum_literal,
  10930         .pointer,
  10931         .@"fn",
  10932         .error_set,
  10933         .@"enum",
  10934         => {
  10935             if (operand_ty.isSlice(zcu)) {
  10936                 return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)});
  10937             }
  10938             if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| {
  10939                 return Air.internedToRef(opv.toIntern());
  10940             }
  10941             return operand;
  10942         },
  10943 
  10944         .@"union" => {
  10945             try operand_ty.resolveFields(pt);
  10946             const enum_ty = operand_ty.unionTagType(zcu) orelse {
  10947                 const msg = msg: {
  10948                     const msg = try sema.errMsg(src, "switch on union with no attached enum", .{});
  10949                     errdefer msg.destroy(sema.gpa);
  10950                     if (operand_ty.srcLocOrNull(zcu)) |union_src| {
  10951                         try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{});
  10952                     }
  10953                     break :msg msg;
  10954                 };
  10955                 return sema.failWithOwnedErrorMsg(block, msg);
  10956             };
  10957             return sema.unionToTag(block, enum_ty, operand, src);
  10958         },
  10959 
  10960         .error_union,
  10961         .noreturn,
  10962         .array,
  10963         .@"struct",
  10964         .undefined,
  10965         .null,
  10966         .optional,
  10967         .@"opaque",
  10968         .vector,
  10969         .frame,
  10970         .@"anyframe",
  10971         => return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)}),
  10972     }
  10973 }
  10974 
  10975 const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, LazySrcLoc);
  10976 
  10977 fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10978     const tracy = trace(@src());
  10979     defer tracy.end();
  10980 
  10981     const pt = sema.pt;
  10982     const zcu = pt.zcu;
  10983     const gpa = sema.gpa;
  10984     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10985     const switch_src = block.nodeOffset(inst_data.src_node);
  10986     const switch_src_node_offset = inst_data.src_node;
  10987     const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset });
  10988     const else_prong_src = block.src(.{ .node_offset_switch_else_prong = switch_src_node_offset });
  10989     const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
  10990     const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset });
  10991     const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset });
  10992 
  10993     const raw_operand_val = try sema.resolveInst(extra.data.operand);
  10994 
  10995     // AstGen guarantees that the instruction immediately preceding
  10996     // switch_block_err_union is a dbg_stmt
  10997     const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
  10998 
  10999     var header_extra_index: usize = extra.end;
  11000 
  11001     const scalar_cases_len = extra.data.bits.scalar_cases_len;
  11002     const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
  11003         const multi_cases_len = sema.code.extra[header_extra_index];
  11004         header_extra_index += 1;
  11005         break :blk multi_cases_len;
  11006     } else 0;
  11007 
  11008     const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
  11009         const err_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
  11010         header_extra_index += 1;
  11011         // SwitchProngAnalysis wants inst_map to have space for the tag capture.
  11012         // Note that the normal capture is referred to via the switch block
  11013         // index, which there is already necessarily space for.
  11014         try sema.inst_map.ensureSpaceForInstructions(gpa, &.{err_capture_inst});
  11015         break :blk err_capture_inst;
  11016     } else undefined;
  11017 
  11018     var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
  11019     defer case_vals.deinit(gpa);
  11020 
  11021     const NonError = struct {
  11022         body: []const Zir.Inst.Index,
  11023         end: usize,
  11024         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  11025     };
  11026 
  11027     const non_error_case: NonError = non_error: {
  11028         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
  11029         const extra_body_start = header_extra_index + 1;
  11030         break :non_error .{
  11031             .body = sema.code.bodySlice(extra_body_start, info.body_len),
  11032             .end = extra_body_start + info.body_len,
  11033             .capture = info.capture,
  11034         };
  11035     };
  11036 
  11037     const Else = struct {
  11038         body: []const Zir.Inst.Index,
  11039         end: usize,
  11040         is_inline: bool,
  11041         has_capture: bool,
  11042     };
  11043 
  11044     const else_case: Else = if (!extra.data.bits.has_else) .{
  11045         .body = &.{},
  11046         .end = non_error_case.end,
  11047         .is_inline = false,
  11048         .has_capture = false,
  11049     } else special: {
  11050         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[non_error_case.end]);
  11051         const extra_body_start = non_error_case.end + 1;
  11052         assert(info.capture != .by_ref);
  11053         assert(!info.has_tag_capture);
  11054         break :special .{
  11055             .body = sema.code.bodySlice(extra_body_start, info.body_len),
  11056             .end = extra_body_start + info.body_len,
  11057             .is_inline = info.is_inline,
  11058             .has_capture = info.capture != .none,
  11059         };
  11060     };
  11061 
  11062     var seen_errors = SwitchErrorSet.init(gpa);
  11063     defer seen_errors.deinit();
  11064 
  11065     const operand_ty = sema.typeOf(raw_operand_val);
  11066     const operand_err_set = if (extra.data.bits.payload_is_ref)
  11067         operand_ty.childType(zcu)
  11068     else
  11069         operand_ty;
  11070 
  11071     if (operand_err_set.zigTypeTag(zcu) != .error_union) {
  11072         return sema.fail(block, switch_src, "expected error union type, found '{f}'", .{
  11073             operand_ty.fmt(pt),
  11074         });
  11075     }
  11076 
  11077     const operand_err_set_ty = operand_err_set.errorUnionSet(zcu);
  11078 
  11079     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  11080     try sema.air_instructions.append(gpa, .{
  11081         .tag = .block,
  11082         .data = undefined,
  11083     });
  11084     var label: Block.Label = .{
  11085         .zir_block = inst,
  11086         .merges = .{
  11087             .src_locs = .{},
  11088             .results = .{},
  11089             .br_list = .{},
  11090             .block_inst = block_inst,
  11091         },
  11092     };
  11093 
  11094     var child_block: Block = .{
  11095         .parent = block,
  11096         .sema = sema,
  11097         .namespace = block.namespace,
  11098         .instructions = .{},
  11099         .label = &label,
  11100         .inlining = block.inlining,
  11101         .comptime_reason = block.comptime_reason,
  11102         .is_typeof = block.is_typeof,
  11103         .c_import_buf = block.c_import_buf,
  11104         .runtime_cond = block.runtime_cond,
  11105         .runtime_loop = block.runtime_loop,
  11106         .runtime_index = block.runtime_index,
  11107         .error_return_trace_index = block.error_return_trace_index,
  11108         .want_safety = block.want_safety,
  11109         .src_base_inst = block.src_base_inst,
  11110         .type_name_ctx = block.type_name_ctx,
  11111     };
  11112     const merges = &child_block.label.?.merges;
  11113     defer child_block.instructions.deinit(gpa);
  11114     defer merges.deinit(gpa);
  11115 
  11116     const resolved_err_set = try sema.resolveInferredErrorSetTy(block, main_src, operand_err_set_ty.toIntern());
  11117     if (Type.fromInterned(resolved_err_set).errorSetIsEmpty(zcu)) {
  11118         return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
  11119     }
  11120 
  11121     const else_error_ty: ?Type = try validateErrSetSwitch(
  11122         sema,
  11123         block,
  11124         &seen_errors,
  11125         &case_vals,
  11126         operand_err_set_ty,
  11127         inst_data,
  11128         scalar_cases_len,
  11129         multi_cases_len,
  11130         .{ .body = else_case.body, .end = else_case.end, .src = else_prong_src },
  11131         extra.data.bits.has_else,
  11132     );
  11133 
  11134     var spa: SwitchProngAnalysis = .{
  11135         .sema = sema,
  11136         .parent_block = block,
  11137         .operand = .{
  11138             .simple = .{
  11139                 .by_val = undefined, // must be set to the unwrapped error code before use
  11140                 .by_ref = undefined,
  11141                 .cond = raw_operand_val,
  11142             },
  11143         },
  11144         .else_error_ty = else_error_ty,
  11145         .switch_block_inst = inst,
  11146         .tag_capture_inst = undefined,
  11147     };
  11148 
  11149     if (try sema.resolveDefinedValue(&child_block, main_src, raw_operand_val)) |ov| {
  11150         const operand_val = if (extra.data.bits.payload_is_ref)
  11151             (try sema.pointerDeref(&child_block, main_src, ov, operand_ty)).?
  11152         else
  11153             ov;
  11154 
  11155         if (operand_val.errorUnionIsPayload(zcu)) {
  11156             return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
  11157         } else {
  11158             const err_val = Value.fromInterned(try pt.intern(.{
  11159                 .err = .{
  11160                     .ty = operand_err_set_ty.toIntern(),
  11161                     .name = operand_val.getErrorName(zcu).unwrap().?,
  11162                 },
  11163             }));
  11164             spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref)
  11165                 try sema.analyzeErrUnionCodePtr(block, switch_operand_src, raw_operand_val)
  11166             else
  11167                 try sema.analyzeErrUnionCode(block, switch_operand_src, raw_operand_val);
  11168 
  11169             if (extra.data.bits.any_uses_err_capture) {
  11170                 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val);
  11171             }
  11172             defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
  11173 
  11174             return resolveSwitchComptime(
  11175                 sema,
  11176                 spa,
  11177                 &child_block,
  11178                 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val),
  11179                 err_val,
  11180                 operand_err_set_ty,
  11181                 switch_src_node_offset,
  11182                 null,
  11183                 .{
  11184                     .body = else_case.body,
  11185                     .end = else_case.end,
  11186                     .capture = if (else_case.has_capture) .by_val else .none,
  11187                     .is_inline = else_case.is_inline,
  11188                     .has_tag_capture = false,
  11189                 },
  11190                 false,
  11191                 case_vals,
  11192                 scalar_cases_len,
  11193                 multi_cases_len,
  11194                 true,
  11195                 false,
  11196             );
  11197         }
  11198     }
  11199 
  11200     if (scalar_cases_len + multi_cases_len == 0) {
  11201         if (else_error_ty) |ty| if (ty.errorSetIsEmpty(zcu)) {
  11202             return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
  11203         };
  11204     }
  11205 
  11206     if (child_block.isComptime()) {
  11207         _ = try sema.resolveConstDefinedValue(&child_block, main_operand_src, raw_operand_val, null);
  11208         unreachable;
  11209     }
  11210 
  11211     const cond = if (extra.data.bits.payload_is_ref) blk: {
  11212         try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val).elemType2(zcu));
  11213         const loaded = try sema.analyzeLoad(block, main_src, raw_operand_val, main_src);
  11214         break :blk try sema.analyzeIsNonErr(block, main_src, loaded);
  11215     } else blk: {
  11216         try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val));
  11217         break :blk try sema.analyzeIsNonErr(block, main_src, raw_operand_val);
  11218     };
  11219 
  11220     var sub_block = child_block.makeSubBlock();
  11221     sub_block.runtime_loop = null;
  11222     sub_block.runtime_cond = main_operand_src;
  11223     sub_block.runtime_index.increment();
  11224     sub_block.need_debug_scope = null; // this body is emitted regardless
  11225     defer sub_block.instructions.deinit(gpa);
  11226 
  11227     const non_error_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body);
  11228     const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
  11229     defer gpa.free(true_instructions);
  11230 
  11231     spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref)
  11232         try sema.analyzeErrUnionCodePtr(&sub_block, switch_operand_src, raw_operand_val)
  11233     else
  11234         try sema.analyzeErrUnionCode(&sub_block, switch_operand_src, raw_operand_val);
  11235 
  11236     if (extra.data.bits.any_uses_err_capture) {
  11237         sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val);
  11238     }
  11239     defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
  11240     _ = try sema.analyzeSwitchRuntimeBlock(
  11241         spa,
  11242         &sub_block,
  11243         switch_src,
  11244         try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val),
  11245         operand_err_set_ty,
  11246         switch_operand_src,
  11247         case_vals,
  11248         .{
  11249             .body = else_case.body,
  11250             .end = else_case.end,
  11251             .capture = if (else_case.has_capture) .by_val else .none,
  11252             .is_inline = else_case.is_inline,
  11253             .has_tag_capture = false,
  11254         },
  11255         scalar_cases_len,
  11256         multi_cases_len,
  11257         false,
  11258         undefined,
  11259         true,
  11260         switch_src_node_offset,
  11261         else_prong_src,
  11262         false,
  11263         undefined,
  11264         seen_errors,
  11265         undefined,
  11266         undefined,
  11267         undefined,
  11268         cond_dbg_node_index,
  11269         true,
  11270         null,
  11271         undefined,
  11272         &.{},
  11273         &.{},
  11274     );
  11275 
  11276     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
  11277         true_instructions.len + sub_block.instructions.items.len);
  11278 
  11279     _ = try child_block.addInst(.{
  11280         .tag = .cond_br,
  11281         .data = .{
  11282             .pl_op = .{
  11283                 .operand = cond,
  11284                 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  11285                     .then_body_len = @intCast(true_instructions.len),
  11286                     .else_body_len = @intCast(sub_block.instructions.items.len),
  11287                     .branch_hints = .{
  11288                         .true = non_error_hint,
  11289                         .false = .none,
  11290                         // Code coverage is desired for error handling.
  11291                         .then_cov = .poi,
  11292                         .else_cov = .poi,
  11293                     },
  11294                 }),
  11295             },
  11296         },
  11297     });
  11298     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
  11299     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  11300 
  11301     return sema.resolveAnalyzedBlock(block, main_src, &child_block, merges, false);
  11302 }
  11303 
  11304 fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
  11305     const tracy = trace(@src());
  11306     defer tracy.end();
  11307 
  11308     const pt = sema.pt;
  11309     const zcu = pt.zcu;
  11310     const ip = &zcu.intern_pool;
  11311     const gpa = sema.gpa;
  11312     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  11313     const src = block.nodeOffset(inst_data.src_node);
  11314     const src_node_offset = inst_data.src_node;
  11315     const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
  11316     const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
  11317     const under_prong_src = block.src(.{ .node_offset_switch_under_prong = src_node_offset });
  11318     const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
  11319 
  11320     const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: {
  11321         const maybe_ptr = try sema.resolveInst(extra.data.operand);
  11322         const val, const ref = if (operand_is_ref)
  11323             .{ try sema.analyzeLoad(block, src, maybe_ptr, operand_src), maybe_ptr }
  11324         else
  11325             .{ maybe_ptr, undefined };
  11326 
  11327         const init_cond = try sema.switchCond(block, operand_src, val);
  11328 
  11329         const operand_ty = sema.typeOf(val);
  11330 
  11331         if (extra.data.bits.has_continue and !block.isComptime()) {
  11332             // Even if the operand is comptime-known, this `switch` is runtime.
  11333             if (try operand_ty.comptimeOnlySema(pt)) {
  11334                 return sema.failWithOwnedErrorMsg(block, msg: {
  11335                     const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)});
  11336                     errdefer msg.destroy(gpa);
  11337                     try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{});
  11338                     break :msg msg;
  11339                 });
  11340             }
  11341             try sema.validateRuntimeValue(block, operand_src, maybe_ptr);
  11342             const operand_alloc = if (extra.data.bits.any_non_inline_capture) a: {
  11343                 const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(maybe_ptr));
  11344                 const operand_alloc = try block.addTy(.alloc, operand_ptr_ty);
  11345                 _ = try block.addBinOp(.store, operand_alloc, maybe_ptr);
  11346                 break :a operand_alloc;
  11347             } else undefined;
  11348             break :op .{
  11349                 .{ .loop = .{
  11350                     .operand_alloc = operand_alloc,
  11351                     .operand_is_ref = operand_is_ref,
  11352                     .init_cond = init_cond,
  11353                 } },
  11354                 operand_ty,
  11355             };
  11356         }
  11357 
  11358         // We always use `simple` in the comptime case, because as far as the dispatching logic
  11359         // is concerned, it really is dispatching a single prong. `resolveSwitchComptime` will
  11360         // be resposible for recursively resolving different prongs as needed.
  11361         break :op .{
  11362             .{ .simple = .{
  11363                 .by_val = val,
  11364                 .by_ref = ref,
  11365                 .cond = init_cond,
  11366             } },
  11367             operand_ty,
  11368         };
  11369     };
  11370 
  11371     const union_originally = raw_operand_ty.zigTypeTag(zcu) == .@"union";
  11372     const err_set = raw_operand_ty.zigTypeTag(zcu) == .error_set;
  11373     const cond_ty = switch (raw_operand_ty.zigTypeTag(zcu)) {
  11374         .@"union" => raw_operand_ty.unionTagType(zcu).?, // validated by `switchCond` above
  11375         else => raw_operand_ty,
  11376     };
  11377 
  11378     // AstGen guarantees that the instruction immediately preceding
  11379     // switch_block(_ref) is a dbg_stmt
  11380     const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
  11381 
  11382     var header_extra_index: usize = extra.end;
  11383 
  11384     const scalar_cases_len = extra.data.bits.scalar_cases_len;
  11385     const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
  11386         const multi_cases_len = sema.code.extra[header_extra_index];
  11387         header_extra_index += 1;
  11388         break :blk multi_cases_len;
  11389     } else 0;
  11390 
  11391     const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
  11392         const tag_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
  11393         header_extra_index += 1;
  11394         // SwitchProngAnalysis wants inst_map to have space for the tag capture.
  11395         // Note that the normal capture is referred to via the switch block
  11396         // index, which there is already necessarily space for.
  11397         try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
  11398         break :blk tag_capture_inst;
  11399     } else undefined;
  11400 
  11401     var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
  11402     defer case_vals.deinit(gpa);
  11403 
  11404     var single_absorbed_item: Zir.Inst.Ref = .none;
  11405     var absorbed_items: []const Zir.Inst.Ref = &.{};
  11406     var absorbed_ranges: []const Zir.Inst.Ref = &.{};
  11407 
  11408     const special_prongs = extra.data.bits.special_prongs;
  11409     const has_else = special_prongs.hasElse();
  11410     const has_under = special_prongs.hasUnder();
  11411     const special_else: SpecialProng = if (has_else) blk: {
  11412         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
  11413         const extra_body_start = header_extra_index + 1;
  11414         break :blk .{
  11415             .body = sema.code.bodySlice(extra_body_start, info.body_len),
  11416             .end = extra_body_start + info.body_len,
  11417             .capture = info.capture,
  11418             .is_inline = info.is_inline,
  11419             .has_tag_capture = info.has_tag_capture,
  11420         };
  11421     } else .{
  11422         .body = &.{},
  11423         .end = header_extra_index,
  11424         .capture = .none,
  11425         .is_inline = false,
  11426         .has_tag_capture = false,
  11427     };
  11428     const special_under: SpecialProng = if (has_under) blk: {
  11429         var extra_index = special_else.end;
  11430         var trailing_items_len: usize = 0;
  11431         if (special_prongs.hasOneAdditionalItem()) {
  11432             single_absorbed_item = @enumFromInt(sema.code.extra[extra_index]);
  11433             extra_index += 1;
  11434             absorbed_items = @ptrCast(&single_absorbed_item);
  11435         } else if (special_prongs.hasManyAdditionalItems()) {
  11436             const items_len = sema.code.extra[extra_index];
  11437             extra_index += 1;
  11438             const ranges_len = sema.code.extra[extra_index];
  11439             extra_index += 1;
  11440             absorbed_items = sema.code.refSlice(extra_index + 1, items_len);
  11441             absorbed_ranges = sema.code.refSlice(extra_index + 1 + items_len, ranges_len * 2);
  11442             trailing_items_len = items_len + ranges_len * 2;
  11443         }
  11444         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11445         extra_index += 1 + trailing_items_len;
  11446         break :blk .{
  11447             .body = sema.code.bodySlice(extra_index, info.body_len),
  11448             .end = extra_index + info.body_len,
  11449             .capture = info.capture,
  11450             .is_inline = info.is_inline,
  11451             .has_tag_capture = info.has_tag_capture,
  11452         };
  11453     } else .{
  11454         .body = &.{},
  11455         .end = special_else.end,
  11456         .capture = .none,
  11457         .is_inline = false,
  11458         .has_tag_capture = false,
  11459     };
  11460     const special_end = special_under.end;
  11461 
  11462     // Duplicate checking variables later also used for `inline else`.
  11463     var seen_enum_fields: []?LazySrcLoc = &.{};
  11464     var seen_errors = SwitchErrorSet.init(gpa);
  11465     var range_set = RangeSet.init(gpa, zcu);
  11466     var true_count: u8 = 0;
  11467     var false_count: u8 = 0;
  11468 
  11469     defer {
  11470         range_set.deinit();
  11471         gpa.free(seen_enum_fields);
  11472         seen_errors.deinit();
  11473     }
  11474 
  11475     var empty_enum = false;
  11476 
  11477     var else_error_ty: ?Type = null;
  11478 
  11479     // Validate usage of '_' prongs.
  11480     if (has_under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) {
  11481         const msg = msg: {
  11482             const msg = try sema.errMsg(
  11483                 src,
  11484                 "'_' prong only allowed when switching on non-exhaustive enums",
  11485                 .{},
  11486             );
  11487             errdefer msg.destroy(gpa);
  11488             try sema.errNote(
  11489                 under_prong_src,
  11490                 msg,
  11491                 "'_' prong here",
  11492                 .{},
  11493             );
  11494             try sema.errNote(
  11495                 src,
  11496                 msg,
  11497                 "consider using 'else'",
  11498                 .{},
  11499             );
  11500             break :msg msg;
  11501         };
  11502         return sema.failWithOwnedErrorMsg(block, msg);
  11503     }
  11504 
  11505     // Validate for duplicate items, missing else prong, and invalid range.
  11506     switch (cond_ty.zigTypeTag(zcu)) {
  11507         .@"union" => unreachable, // handled in `switchCond`
  11508         .@"enum" => {
  11509             seen_enum_fields = try gpa.alloc(?LazySrcLoc, cond_ty.enumFieldCount(zcu));
  11510             empty_enum = seen_enum_fields.len == 0 and !cond_ty.isNonexhaustiveEnum(zcu);
  11511             @memset(seen_enum_fields, null);
  11512             // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
  11513 
  11514             for (absorbed_items, 0..) |item_ref, item_i| {
  11515                 _ = try sema.validateSwitchItemEnum(
  11516                     block,
  11517                     seen_enum_fields,
  11518                     &range_set,
  11519                     item_ref,
  11520                     cond_ty,
  11521                     block.src(.{ .switch_case_item = .{
  11522                         .switch_node_offset = src_node_offset,
  11523                         .case_idx = .special_under,
  11524                         .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  11525                     } }),
  11526                 );
  11527             }
  11528             try sema.validateSwitchNoRange(block, @intCast(absorbed_ranges.len), cond_ty, src_node_offset);
  11529 
  11530             var extra_index: usize = special_end;
  11531             {
  11532                 var scalar_i: u32 = 0;
  11533                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11534                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11535                     extra_index += 1;
  11536                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11537                     extra_index += 1 + info.body_len;
  11538 
  11539                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
  11540                         block,
  11541                         seen_enum_fields,
  11542                         &range_set,
  11543                         item_ref,
  11544                         cond_ty,
  11545                         block.src(.{ .switch_case_item = .{
  11546                             .switch_node_offset = src_node_offset,
  11547                             .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  11548                             .item_idx = .{ .kind = .single, .index = 0 },
  11549                         } }),
  11550                     ));
  11551                 }
  11552             }
  11553             {
  11554                 var multi_i: u32 = 0;
  11555                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11556                     const items_len = sema.code.extra[extra_index];
  11557                     extra_index += 1;
  11558                     const ranges_len = sema.code.extra[extra_index];
  11559                     extra_index += 1;
  11560                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11561                     extra_index += 1;
  11562                     const items = sema.code.refSlice(extra_index, items_len);
  11563                     extra_index += items_len + info.body_len;
  11564 
  11565                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11566                     for (items, 0..) |item_ref, item_i| {
  11567                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
  11568                             block,
  11569                             seen_enum_fields,
  11570                             &range_set,
  11571                             item_ref,
  11572                             cond_ty,
  11573                             block.src(.{ .switch_case_item = .{
  11574                                 .switch_node_offset = src_node_offset,
  11575                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11576                                 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  11577                             } }),
  11578                         ));
  11579                     }
  11580 
  11581                     try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
  11582                 }
  11583             }
  11584             const all_tags_handled = for (seen_enum_fields) |seen_src| {
  11585                 if (seen_src == null) break false;
  11586             } else true;
  11587 
  11588             if (has_else) {
  11589                 if (all_tags_handled) {
  11590                     if (cond_ty.isNonexhaustiveEnum(zcu)) {
  11591                         if (has_under) return sema.fail(
  11592                             block,
  11593                             else_prong_src,
  11594                             "unreachable else prong; all explicit cases already handled",
  11595                             .{},
  11596                         );
  11597                     } else return sema.fail(
  11598                         block,
  11599                         else_prong_src,
  11600                         "unreachable else prong; all cases already handled",
  11601                         .{},
  11602                     );
  11603                 }
  11604             } else if (!all_tags_handled) {
  11605                 const msg = msg: {
  11606                     const msg = try sema.errMsg(
  11607                         src,
  11608                         "switch must handle all possibilities",
  11609                         .{},
  11610                     );
  11611                     errdefer msg.destroy(sema.gpa);
  11612                     for (seen_enum_fields, 0..) |seen_src, i| {
  11613                         if (seen_src != null) continue;
  11614 
  11615                         const field_name = cond_ty.enumFieldName(i, zcu);
  11616                         try sema.addFieldErrNote(
  11617                             cond_ty,
  11618                             i,
  11619                             msg,
  11620                             "unhandled enumeration value: '{f}'",
  11621                             .{field_name.fmt(ip)},
  11622                         );
  11623                     }
  11624                     try sema.errNote(
  11625                         cond_ty.srcLoc(zcu),
  11626                         msg,
  11627                         "enum '{f}' declared here",
  11628                         .{cond_ty.fmt(pt)},
  11629                     );
  11630                     break :msg msg;
  11631                 };
  11632                 return sema.failWithOwnedErrorMsg(block, msg);
  11633             } else if (special_prongs == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
  11634                 return sema.fail(
  11635                     block,
  11636                     src,
  11637                     "switch on non-exhaustive enum must include 'else' or '_' prong or both",
  11638                     .{},
  11639                 );
  11640             }
  11641         },
  11642         .error_set => else_error_ty = try validateErrSetSwitch(
  11643             sema,
  11644             block,
  11645             &seen_errors,
  11646             &case_vals,
  11647             cond_ty,
  11648             inst_data,
  11649             scalar_cases_len,
  11650             multi_cases_len,
  11651             .{ .body = special_else.body, .end = special_else.end, .src = else_prong_src },
  11652             has_else,
  11653         ),
  11654         .int, .comptime_int => {
  11655             var extra_index: usize = special_end;
  11656             {
  11657                 var scalar_i: u32 = 0;
  11658                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11659                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11660                     extra_index += 1;
  11661                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11662                     extra_index += 1 + info.body_len;
  11663 
  11664                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
  11665                         block,
  11666                         &range_set,
  11667                         item_ref,
  11668                         cond_ty,
  11669                         block.src(.{ .switch_case_item = .{
  11670                             .switch_node_offset = src_node_offset,
  11671                             .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  11672                             .item_idx = .{ .kind = .single, .index = 0 },
  11673                         } }),
  11674                     ));
  11675                 }
  11676             }
  11677             {
  11678                 var multi_i: u32 = 0;
  11679                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11680                     const items_len = sema.code.extra[extra_index];
  11681                     extra_index += 1;
  11682                     const ranges_len = sema.code.extra[extra_index];
  11683                     extra_index += 1;
  11684                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11685                     extra_index += 1;
  11686                     const items = sema.code.refSlice(extra_index, items_len);
  11687                     extra_index += items_len;
  11688 
  11689                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11690                     for (items, 0..) |item_ref, item_i| {
  11691                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
  11692                             block,
  11693                             &range_set,
  11694                             item_ref,
  11695                             cond_ty,
  11696                             block.src(.{ .switch_case_item = .{
  11697                                 .switch_node_offset = src_node_offset,
  11698                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11699                                 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  11700                             } }),
  11701                         ));
  11702                     }
  11703 
  11704                     try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len);
  11705                     var range_i: u32 = 0;
  11706                     while (range_i < ranges_len) : (range_i += 1) {
  11707                         const item_first: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11708                         extra_index += 1;
  11709                         const item_last: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11710                         extra_index += 1;
  11711 
  11712                         const vals = try sema.validateSwitchRange(
  11713                             block,
  11714                             &range_set,
  11715                             item_first,
  11716                             item_last,
  11717                             cond_ty,
  11718                             block.src(.{ .switch_case_item = .{
  11719                                 .switch_node_offset = src_node_offset,
  11720                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11721                                 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
  11722                             } }),
  11723                         );
  11724                         case_vals.appendAssumeCapacity(vals[0]);
  11725                         case_vals.appendAssumeCapacity(vals[1]);
  11726                     }
  11727 
  11728                     extra_index += info.body_len;
  11729                 }
  11730             }
  11731 
  11732             check_range: {
  11733                 if (cond_ty.zigTypeTag(zcu) == .int) {
  11734                     const min_int = try cond_ty.minInt(pt, cond_ty);
  11735                     const max_int = try cond_ty.maxInt(pt, cond_ty);
  11736                     if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
  11737                         if (has_else) {
  11738                             return sema.fail(
  11739                                 block,
  11740                                 else_prong_src,
  11741                                 "unreachable else prong; all cases already handled",
  11742                                 .{},
  11743                             );
  11744                         }
  11745                         break :check_range;
  11746                     }
  11747                 }
  11748                 if (special_prongs == .none) {
  11749                     return sema.fail(
  11750                         block,
  11751                         src,
  11752                         "switch must handle all possibilities",
  11753                         .{},
  11754                     );
  11755                 }
  11756             }
  11757         },
  11758         .bool => {
  11759             var extra_index: usize = special_end;
  11760             {
  11761                 var scalar_i: u32 = 0;
  11762                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11763                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11764                     extra_index += 1;
  11765                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11766                     extra_index += 1 + info.body_len;
  11767 
  11768                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
  11769                         block,
  11770                         &true_count,
  11771                         &false_count,
  11772                         item_ref,
  11773                         block.src(.{ .switch_case_item = .{
  11774                             .switch_node_offset = src_node_offset,
  11775                             .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  11776                             .item_idx = .{ .kind = .single, .index = 0 },
  11777                         } }),
  11778                     ));
  11779                 }
  11780             }
  11781             {
  11782                 var multi_i: u32 = 0;
  11783                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11784                     const items_len = sema.code.extra[extra_index];
  11785                     extra_index += 1;
  11786                     const ranges_len = sema.code.extra[extra_index];
  11787                     extra_index += 1;
  11788                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11789                     extra_index += 1;
  11790                     const items = sema.code.refSlice(extra_index, items_len);
  11791                     extra_index += items_len + info.body_len;
  11792 
  11793                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11794                     for (items, 0..) |item_ref, item_i| {
  11795                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
  11796                             block,
  11797                             &true_count,
  11798                             &false_count,
  11799                             item_ref,
  11800                             block.src(.{ .switch_case_item = .{
  11801                                 .switch_node_offset = src_node_offset,
  11802                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11803                                 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  11804                             } }),
  11805                         ));
  11806                     }
  11807 
  11808                     try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
  11809                 }
  11810             }
  11811             if (has_else) {
  11812                 if (true_count + false_count == 2) {
  11813                     return sema.fail(
  11814                         block,
  11815                         else_prong_src,
  11816                         "unreachable else prong; all cases already handled",
  11817                         .{},
  11818                     );
  11819                 }
  11820             } else {
  11821                 if (true_count + false_count < 2) {
  11822                     return sema.fail(
  11823                         block,
  11824                         src,
  11825                         "switch must handle all possibilities",
  11826                         .{},
  11827                     );
  11828                 }
  11829             }
  11830         },
  11831         .enum_literal, .void, .@"fn", .pointer, .type => {
  11832             if (!has_else) {
  11833                 return sema.fail(
  11834                     block,
  11835                     src,
  11836                     "else prong required when switching on type '{f}'",
  11837                     .{cond_ty.fmt(pt)},
  11838                 );
  11839             }
  11840 
  11841             var seen_values = ValueSrcMap{};
  11842             defer seen_values.deinit(gpa);
  11843 
  11844             var extra_index: usize = special_end;
  11845             {
  11846                 var scalar_i: u32 = 0;
  11847                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11848                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11849                     extra_index += 1;
  11850                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11851                     extra_index += 1;
  11852                     extra_index += info.body_len;
  11853 
  11854                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
  11855                         block,
  11856                         &seen_values,
  11857                         item_ref,
  11858                         cond_ty,
  11859                         block.src(.{ .switch_case_item = .{
  11860                             .switch_node_offset = src_node_offset,
  11861                             .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  11862                             .item_idx = .{ .kind = .single, .index = 0 },
  11863                         } }),
  11864                     ));
  11865                 }
  11866             }
  11867             {
  11868                 var multi_i: u32 = 0;
  11869                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11870                     const items_len = sema.code.extra[extra_index];
  11871                     extra_index += 1;
  11872                     const ranges_len = sema.code.extra[extra_index];
  11873                     extra_index += 1;
  11874                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11875                     extra_index += 1;
  11876                     const items = sema.code.refSlice(extra_index, items_len);
  11877                     extra_index += items_len + info.body_len;
  11878 
  11879                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11880                     for (items, 0..) |item_ref, item_i| {
  11881                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
  11882                             block,
  11883                             &seen_values,
  11884                             item_ref,
  11885                             cond_ty,
  11886                             block.src(.{ .switch_case_item = .{
  11887                                 .switch_node_offset = src_node_offset,
  11888                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11889                                 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  11890                             } }),
  11891                         ));
  11892                     }
  11893 
  11894                     try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
  11895                 }
  11896             }
  11897         },
  11898 
  11899         .error_union,
  11900         .noreturn,
  11901         .array,
  11902         .@"struct",
  11903         .undefined,
  11904         .null,
  11905         .optional,
  11906         .@"opaque",
  11907         .vector,
  11908         .frame,
  11909         .@"anyframe",
  11910         .comptime_float,
  11911         .float,
  11912         => return sema.fail(block, operand_src, "invalid switch operand type '{f}'", .{
  11913             raw_operand_ty.fmt(pt),
  11914         }),
  11915     }
  11916 
  11917     var special_members_only: ?SpecialProng = null;
  11918     var special_members_only_src: LazySrcLoc = undefined;
  11919     const special_generic, const special_generic_src = if (has_under) b: {
  11920         if (has_else) {
  11921             special_members_only = special_else;
  11922             special_members_only_src = else_prong_src;
  11923         }
  11924         break :b .{ special_under, under_prong_src };
  11925     } else .{ special_else, else_prong_src };
  11926 
  11927     const spa: SwitchProngAnalysis = .{
  11928         .sema = sema,
  11929         .parent_block = block,
  11930         .operand = operand,
  11931         .else_error_ty = else_error_ty,
  11932         .switch_block_inst = inst,
  11933         .tag_capture_inst = tag_capture_inst,
  11934     };
  11935 
  11936     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  11937     try sema.air_instructions.append(gpa, .{
  11938         .tag = .block,
  11939         .data = undefined,
  11940     });
  11941     var label: Block.Label = .{
  11942         .zir_block = inst,
  11943         .merges = .{
  11944             .src_locs = .{},
  11945             .results = .{},
  11946             .br_list = .{},
  11947             .block_inst = block_inst,
  11948         },
  11949     };
  11950 
  11951     var child_block: Block = .{
  11952         .parent = block,
  11953         .sema = sema,
  11954         .namespace = block.namespace,
  11955         .instructions = .{},
  11956         .label = &label,
  11957         .inlining = block.inlining,
  11958         .comptime_reason = block.comptime_reason,
  11959         .is_typeof = block.is_typeof,
  11960         .c_import_buf = block.c_import_buf,
  11961         .runtime_cond = block.runtime_cond,
  11962         .runtime_loop = block.runtime_loop,
  11963         .runtime_index = block.runtime_index,
  11964         .want_safety = block.want_safety,
  11965         .error_return_trace_index = block.error_return_trace_index,
  11966         .src_base_inst = block.src_base_inst,
  11967         .type_name_ctx = block.type_name_ctx,
  11968     };
  11969     const merges = &child_block.label.?.merges;
  11970     defer child_block.instructions.deinit(gpa);
  11971     defer merges.deinit(gpa);
  11972 
  11973     if (scalar_cases_len + multi_cases_len == 0 and
  11974         special_members_only == null and
  11975         !special_generic.is_inline)
  11976     {
  11977         if (empty_enum) {
  11978             return .void_value;
  11979         }
  11980         if (special_prongs == .none) {
  11981             return sema.fail(block, src, "switch must handle all possibilities", .{});
  11982         }
  11983         const init_cond = switch (operand) {
  11984             .simple => |s| s.cond,
  11985             .loop => |l| l.init_cond,
  11986         };
  11987         if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
  11988             raw_operand_ty.zigTypeTag(zcu) == .@"enum" and !raw_operand_ty.isNonexhaustiveEnum(zcu))
  11989         {
  11990             try sema.zirDbgStmt(block, cond_dbg_node_index);
  11991             const ok = try block.addUnOp(.is_named_enum_value, init_cond);
  11992             try sema.addSafetyCheck(block, src, ok, .corrupt_switch);
  11993         }
  11994         if (err_set and try sema.maybeErrorUnwrap(block, special_generic.body, init_cond, operand_src, false)) {
  11995             return .unreachable_value;
  11996         }
  11997     }
  11998 
  11999     switch (operand) {
  12000         .loop => {}, // always runtime; evaluation in comptime scope uses `simple`
  12001         .simple => |s| {
  12002             if (try sema.resolveDefinedValue(&child_block, src, s.cond)) |cond_val| {
  12003                 return resolveSwitchComptimeLoop(
  12004                     sema,
  12005                     spa,
  12006                     &child_block,
  12007                     if (operand_is_ref)
  12008                         sema.typeOf(s.by_ref)
  12009                     else
  12010                         raw_operand_ty,
  12011                     cond_ty,
  12012                     cond_val,
  12013                     src_node_offset,
  12014                     special_members_only,
  12015                     special_generic,
  12016                     has_under,
  12017                     case_vals,
  12018                     scalar_cases_len,
  12019                     multi_cases_len,
  12020                     err_set,
  12021                     empty_enum,
  12022                     operand_is_ref,
  12023                 );
  12024             }
  12025 
  12026             if (scalar_cases_len + multi_cases_len == 0 and
  12027                 special_members_only == null and
  12028                 !special_generic.is_inline and
  12029                 !extra.data.bits.has_continue)
  12030             {
  12031                 return spa.resolveProngComptime(
  12032                     &child_block,
  12033                     .special,
  12034                     special_generic.body,
  12035                     special_generic.capture,
  12036                     block.src(.{ .switch_capture = .{
  12037                         .switch_node_offset = src_node_offset,
  12038                         .case_idx = if (has_under) .special_under else .special_else,
  12039                     } }),
  12040                     undefined, // case_vals may be undefined for special prongs
  12041                     .none,
  12042                     false,
  12043                     merges,
  12044                 );
  12045             }
  12046         },
  12047     }
  12048 
  12049     if (child_block.isComptime()) {
  12050         _ = try sema.resolveConstDefinedValue(&child_block, operand_src, operand.simple.cond, null);
  12051         unreachable;
  12052     }
  12053 
  12054     var extra_case_vals: struct {
  12055         items: std.ArrayListUnmanaged(Air.Inst.Ref),
  12056         ranges: std.ArrayListUnmanaged([2]Air.Inst.Ref),
  12057     } = .{ .items = .empty, .ranges = .empty };
  12058     defer {
  12059         extra_case_vals.items.deinit(gpa);
  12060         extra_case_vals.ranges.deinit(gpa);
  12061     }
  12062 
  12063     // Runtime switch, if we have a special_members_only prong we need to unroll
  12064     // it to a prong with explicit items.
  12065     // Although this is potentially the same as `inline else` it does not count
  12066     // towards the backward branch quota because it's an implementation detail.
  12067     if (special_members_only != null) gen: {
  12068         assert(cond_ty.isNonexhaustiveEnum(zcu));
  12069 
  12070         var min_i: usize = math.maxInt(usize);
  12071         var max_i: usize = 0;
  12072         var seen_field_count: usize = 0;
  12073         for (seen_enum_fields, 0..) |seen, enum_i| {
  12074             if (seen != null) {
  12075                 seen_field_count += 1;
  12076             } else {
  12077                 min_i = @min(min_i, enum_i);
  12078                 max_i = @max(max_i, enum_i);
  12079             }
  12080         }
  12081         if (min_i == max_i) {
  12082             seen_enum_fields[min_i] = special_members_only_src;
  12083             const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
  12084             const item_ref = Air.internedToRef(item_val.toIntern());
  12085             try extra_case_vals.items.append(gpa, item_ref);
  12086             break :gen;
  12087         }
  12088         const missing_field_count = seen_enum_fields.len - seen_field_count;
  12089 
  12090         extra_case_vals.items = try .initCapacity(gpa, missing_field_count / 2);
  12091         extra_case_vals.ranges = try .initCapacity(gpa, missing_field_count / 4);
  12092         const int_ty = cond_ty.intTagType(zcu);
  12093 
  12094         var last_val = try pt.enumValueFieldIndex(cond_ty, @intCast(min_i));
  12095         var first_ref = Air.internedToRef(last_val.toIntern());
  12096         seen_enum_fields[min_i] = special_members_only_src;
  12097         for (seen_enum_fields[(min_i + 1)..(max_i + 1)], (min_i + 1)..) |seen, enum_i| {
  12098             if (seen != null) continue;
  12099             seen_enum_fields[enum_i] = special_members_only_src;
  12100 
  12101             const item_val = try pt.enumValueFieldIndex(cond_ty, @intCast(enum_i));
  12102             const item_ref = Air.internedToRef(item_val.toIntern());
  12103 
  12104             const is_next = is_next: {
  12105                 const prev_int = ip.indexToKey(last_val.toIntern()).enum_tag.int;
  12106 
  12107                 const result = try arith.incrementDefinedInt(sema, int_ty, .fromInterned(prev_int));
  12108                 if (result.overflow) break :is_next false;
  12109 
  12110                 const item_int = ip.indexToKey(item_val.toIntern()).enum_tag.int;
  12111                 break :is_next try sema.valuesEqual(.fromInterned(item_int), result.val, int_ty);
  12112             };
  12113 
  12114             if (is_next) {
  12115                 last_val = item_val;
  12116             } else {
  12117                 const last_ref = Air.internedToRef(last_val.toIntern());
  12118                 if (first_ref == last_ref) {
  12119                     try extra_case_vals.items.append(gpa, first_ref);
  12120                 } else {
  12121                     try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
  12122                 }
  12123                 first_ref = item_ref;
  12124                 last_val = item_val;
  12125             }
  12126         }
  12127         const last_ref = Air.internedToRef(last_val.toIntern());
  12128         if (first_ref == last_ref) {
  12129             try extra_case_vals.items.append(gpa, first_ref);
  12130         } else {
  12131             try extra_case_vals.ranges.append(gpa, .{ first_ref, last_ref });
  12132         }
  12133     }
  12134 
  12135     const air_switch_ref = try sema.analyzeSwitchRuntimeBlock(
  12136         spa,
  12137         &child_block,
  12138         src,
  12139         switch (operand) {
  12140             .simple => |s| s.cond,
  12141             .loop => |l| l.init_cond,
  12142         },
  12143         cond_ty,
  12144         operand_src,
  12145         case_vals,
  12146         special_generic,
  12147         scalar_cases_len,
  12148         multi_cases_len,
  12149         union_originally,
  12150         raw_operand_ty,
  12151         err_set,
  12152         src_node_offset,
  12153         special_generic_src,
  12154         has_under,
  12155         seen_enum_fields,
  12156         seen_errors,
  12157         range_set,
  12158         true_count,
  12159         false_count,
  12160         cond_dbg_node_index,
  12161         false,
  12162         special_members_only,
  12163         special_members_only_src,
  12164         extra_case_vals.items.items,
  12165         extra_case_vals.ranges.items,
  12166     );
  12167 
  12168     for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
  12169         var replacement_block = block.makeSubBlock();
  12170         defer replacement_block.instructions.deinit(gpa);
  12171 
  12172         assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br);
  12173         const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand;
  12174 
  12175         if (extra.data.bits.any_non_inline_capture) {
  12176             _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref);
  12177         }
  12178 
  12179         const new_operand_val = if (operand_is_ref)
  12180             try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src)
  12181         else
  12182             new_operand_maybe_ref;
  12183 
  12184         const new_cond = try sema.switchCond(&replacement_block, dispatch_src, new_operand_val);
  12185 
  12186         if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
  12187             cond_ty.zigTypeTag(zcu) == .@"enum" and !cond_ty.isNonexhaustiveEnum(zcu) and
  12188             !try sema.isComptimeKnown(new_cond))
  12189         {
  12190             const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond);
  12191             try sema.addSafetyCheck(&replacement_block, src, ok, .corrupt_switch);
  12192         }
  12193 
  12194         _ = try replacement_block.addInst(.{
  12195             .tag = .switch_dispatch,
  12196             .data = .{ .br = .{
  12197                 .block_inst = air_switch_ref.toIndex().?,
  12198                 .operand = new_cond,
  12199             } },
  12200         });
  12201 
  12202         if (replacement_block.instructions.items.len == 1) {
  12203             // Optimization: we don't need a block!
  12204             sema.air_instructions.set(
  12205                 @intFromEnum(placeholder_inst),
  12206                 sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])),
  12207             );
  12208             continue;
  12209         }
  12210 
  12211         // Replace placeholder with a block.
  12212         // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`.
  12213         try sema.air_extra.ensureUnusedCapacity(
  12214             gpa,
  12215             @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len,
  12216         );
  12217         sema.air_instructions.set(@intFromEnum(placeholder_inst), .{
  12218             .tag = .block,
  12219             .data = .{ .ty_pl = .{
  12220                 .ty = .noreturn_type,
  12221                 .payload = sema.addExtraAssumeCapacity(Air.Block{
  12222                     .body_len = @intCast(replacement_block.instructions.items.len),
  12223                 }),
  12224             } },
  12225         });
  12226         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
  12227     }
  12228 
  12229     return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
  12230 }
  12231 
  12232 const SpecialProng = struct {
  12233     body: []const Zir.Inst.Index,
  12234     end: usize,
  12235     capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  12236     is_inline: bool,
  12237     has_tag_capture: bool,
  12238 };
  12239 
  12240 fn analyzeSwitchRuntimeBlock(
  12241     sema: *Sema,
  12242     spa: SwitchProngAnalysis,
  12243     child_block: *Block,
  12244     src: LazySrcLoc,
  12245     operand: Air.Inst.Ref,
  12246     operand_ty: Type,
  12247     operand_src: LazySrcLoc,
  12248     case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
  12249     else_prong: SpecialProng,
  12250     scalar_cases_len: usize,
  12251     multi_cases_len: usize,
  12252     union_originally: bool,
  12253     maybe_union_ty: Type,
  12254     err_set: bool,
  12255     switch_node_offset: std.zig.Ast.Node.Offset,
  12256     else_prong_src: LazySrcLoc,
  12257     else_prong_is_underscore: bool,
  12258     seen_enum_fields: []?LazySrcLoc,
  12259     seen_errors: SwitchErrorSet,
  12260     range_set: RangeSet,
  12261     true_count: u8,
  12262     false_count: u8,
  12263     cond_dbg_node_index: Zir.Inst.Index,
  12264     allow_err_code_unwrap: bool,
  12265     extra_prong: ?SpecialProng,
  12266     /// May be `undefined` if `extra_prong` is `null`
  12267     extra_prong_src: LazySrcLoc,
  12268     extra_prong_items: []const Air.Inst.Ref,
  12269     extra_prong_ranges: []const [2]Air.Inst.Ref,
  12270 ) CompileError!Air.Inst.Ref {
  12271     const pt = sema.pt;
  12272     const zcu = pt.zcu;
  12273     const gpa = sema.gpa;
  12274     const ip = &zcu.intern_pool;
  12275 
  12276     const block = child_block.parent.?;
  12277 
  12278     const estimated_cases_extra = (scalar_cases_len + multi_cases_len) *
  12279         @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 2;
  12280     var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra);
  12281     defer cases_extra.deinit(gpa);
  12282 
  12283     var branch_hints = try std.ArrayListUnmanaged(std.builtin.BranchHint).initCapacity(gpa, scalar_cases_len);
  12284     defer branch_hints.deinit(gpa);
  12285 
  12286     var case_block = child_block.makeSubBlock();
  12287     case_block.runtime_loop = null;
  12288     case_block.runtime_cond = operand_src;
  12289     case_block.runtime_index.increment();
  12290     case_block.need_debug_scope = null; // this body is emitted regardless
  12291     defer case_block.instructions.deinit(gpa);
  12292 
  12293     var extra_index: usize = else_prong.end;
  12294 
  12295     var scalar_i: usize = 0;
  12296     while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  12297         extra_index += 1;
  12298         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  12299         extra_index += 1;
  12300         const body = sema.code.bodySlice(extra_index, info.body_len);
  12301         extra_index += info.body_len;
  12302 
  12303         case_block.instructions.shrinkRetainingCapacity(0);
  12304         case_block.error_return_trace_index = child_block.error_return_trace_index;
  12305 
  12306         const item = case_vals.items[scalar_i];
  12307         // `item` is already guaranteed to be constant known.
  12308 
  12309         const analyze_body = if (union_originally) blk: {
  12310             const unresolved_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  12311             const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
  12312             const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
  12313             break :blk field_ty.zigTypeTag(zcu) != .noreturn;
  12314         } else true;
  12315 
  12316         const prong_hint: std.builtin.BranchHint = if (err_set and
  12317             try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
  12318         h: {
  12319             // nothing to do here. weight against error branch
  12320             break :h .unlikely;
  12321         } else if (analyze_body) h: {
  12322             break :h try spa.analyzeProngRuntime(
  12323                 &case_block,
  12324                 .normal,
  12325                 body,
  12326                 info.capture,
  12327                 child_block.src(.{ .switch_capture = .{
  12328                     .switch_node_offset = switch_node_offset,
  12329                     .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  12330                 } }),
  12331                 &.{item},
  12332                 if (info.is_inline) item else .none,
  12333                 info.has_tag_capture,
  12334             );
  12335         } else h: {
  12336             _ = try case_block.addNoOp(.unreach);
  12337             break :h .none;
  12338         };
  12339 
  12340         try branch_hints.append(gpa, prong_hint);
  12341         try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12342             1 + // `item`, no ranges
  12343             case_block.instructions.items.len);
  12344         cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12345             .items_len = 1,
  12346             .ranges_len = 0,
  12347             .body_len = @intCast(case_block.instructions.items.len),
  12348         }));
  12349         cases_extra.appendAssumeCapacity(@intFromEnum(item));
  12350         cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12351     }
  12352 
  12353     var cases_len = scalar_cases_len;
  12354     var case_val_idx: usize = scalar_cases_len;
  12355     const multi_cases_len_with_extra_prong = multi_cases_len + @intFromBool(extra_prong != null);
  12356     var multi_i: u32 = 0;
  12357     while (multi_i < multi_cases_len_with_extra_prong) : (multi_i += 1) {
  12358         const is_extra_prong = multi_i == multi_cases_len;
  12359         var items: []const Air.Inst.Ref = undefined;
  12360         var info: Zir.Inst.SwitchBlock.ProngInfo = undefined;
  12361         var ranges: []const [2]Air.Inst.Ref = undefined;
  12362         var body: []const Zir.Inst.Index = undefined;
  12363         if (is_extra_prong) {
  12364             const prong = extra_prong.?;
  12365             items = extra_prong_items;
  12366             ranges = extra_prong_ranges;
  12367             body = prong.body;
  12368             info = .{
  12369                 .body_len = undefined,
  12370                 .capture = prong.capture,
  12371                 .is_inline = prong.is_inline,
  12372                 .has_tag_capture = prong.has_tag_capture,
  12373             };
  12374         } else {
  12375             @branchHint(.likely);
  12376             const items_len = sema.code.extra[extra_index];
  12377             extra_index += 1;
  12378             const ranges_len = sema.code.extra[extra_index];
  12379             extra_index += 1;
  12380             info = @bitCast(sema.code.extra[extra_index]);
  12381             extra_index += 1 + items_len + ranges_len * 2;
  12382 
  12383             items = case_vals.items[case_val_idx..][0..items_len];
  12384             case_val_idx += items_len;
  12385             ranges = @ptrCast(case_vals.items[case_val_idx..][0 .. ranges_len * 2]);
  12386             case_val_idx += ranges_len * 2;
  12387 
  12388             body = sema.code.bodySlice(extra_index, info.body_len);
  12389             extra_index += info.body_len;
  12390         }
  12391 
  12392         case_block.instructions.shrinkRetainingCapacity(0);
  12393         case_block.error_return_trace_index = child_block.error_return_trace_index;
  12394 
  12395         // Generate all possible cases as scalar prongs.
  12396         if (info.is_inline) {
  12397             var emit_bb = false;
  12398 
  12399             for (ranges, 0..) |range_items, range_i| {
  12400                 var item = sema.resolveConstDefinedValue(block, .unneeded, range_items[0], undefined) catch unreachable;
  12401                 const item_last = sema.resolveConstDefinedValue(block, .unneeded, range_items[1], undefined) catch unreachable;
  12402 
  12403                 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
  12404                     // Previous validation has resolved any possible lazy values.
  12405                     const int_val: Value, const int_ty: Type = switch (operand_ty.zigTypeTag(zcu)) {
  12406                         .int => .{ item, operand_ty },
  12407                         .@"enum" => b: {
  12408                             const int_val = Value.fromInterned(ip.indexToKey(item.toIntern()).enum_tag.int);
  12409                             break :b .{ int_val, int_val.typeOf(zcu) };
  12410                         },
  12411                         else => unreachable,
  12412                     };
  12413                     const result = try arith.incrementDefinedInt(sema, int_ty, int_val);
  12414                     assert(!result.overflow);
  12415                     item = switch (operand_ty.zigTypeTag(zcu)) {
  12416                         .int => result.val,
  12417                         .@"enum" => .fromInterned(try pt.intern(.{ .enum_tag = .{
  12418                             .ty = operand_ty.toIntern(),
  12419                             .int = result.val.toIntern(),
  12420                         } })),
  12421                         else => unreachable,
  12422                     };
  12423                 }) {
  12424                     cases_len += 1;
  12425 
  12426                     const item_ref = Air.internedToRef(item.toIntern());
  12427 
  12428                     case_block.instructions.shrinkRetainingCapacity(0);
  12429                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12430 
  12431                     if (emit_bb) {
  12432                         const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
  12433                             .switch_node_offset = switch_node_offset,
  12434                             .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12435                             .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
  12436                         } });
  12437                         try sema.emitBackwardBranch(block, bb_src);
  12438                     }
  12439                     emit_bb = true;
  12440 
  12441                     const prong_hint = try spa.analyzeProngRuntime(
  12442                         &case_block,
  12443                         .normal,
  12444                         body,
  12445                         info.capture,
  12446                         child_block.src(.{ .switch_capture = .{
  12447                             .switch_node_offset = switch_node_offset,
  12448                             .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12449                         } }),
  12450                         undefined, // case_vals may be undefined for ranges
  12451                         item_ref,
  12452                         info.has_tag_capture,
  12453                     );
  12454                     try branch_hints.append(gpa, prong_hint);
  12455 
  12456                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12457                         1 + // `item`, no ranges
  12458                         case_block.instructions.items.len);
  12459                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12460                         .items_len = 1,
  12461                         .ranges_len = 0,
  12462                         .body_len = @intCast(case_block.instructions.items.len),
  12463                     }));
  12464                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12465                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12466 
  12467                     if (item.compareScalar(.eq, item_last, operand_ty, zcu)) break;
  12468                 }
  12469             }
  12470 
  12471             for (items, 0..) |item, item_i| {
  12472                 cases_len += 1;
  12473 
  12474                 case_block.instructions.shrinkRetainingCapacity(0);
  12475                 case_block.error_return_trace_index = child_block.error_return_trace_index;
  12476 
  12477                 const analyze_body = if (union_originally) blk: {
  12478                     const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  12479                     const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
  12480                     break :blk field_ty.zigTypeTag(zcu) != .noreturn;
  12481                 } else true;
  12482 
  12483                 if (emit_bb) {
  12484                     const bb_src = if (is_extra_prong) extra_prong_src else block.src(.{ .switch_case_item = .{
  12485                         .switch_node_offset = switch_node_offset,
  12486                         .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12487                         .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  12488                     } });
  12489                     try sema.emitBackwardBranch(block, bb_src);
  12490                 }
  12491                 emit_bb = true;
  12492 
  12493                 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
  12494                     break :h try spa.analyzeProngRuntime(
  12495                         &case_block,
  12496                         .normal,
  12497                         body,
  12498                         info.capture,
  12499                         child_block.src(.{ .switch_capture = .{
  12500                             .switch_node_offset = switch_node_offset,
  12501                             .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12502                         } }),
  12503                         &.{item},
  12504                         item,
  12505                         info.has_tag_capture,
  12506                     );
  12507                 } else h: {
  12508                     _ = try case_block.addNoOp(.unreach);
  12509                     break :h .none;
  12510                 };
  12511                 try branch_hints.append(gpa, prong_hint);
  12512 
  12513                 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12514                     1 + // `item`, no ranges
  12515                     case_block.instructions.items.len);
  12516                 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12517                     .items_len = 1,
  12518                     .ranges_len = 0,
  12519                     .body_len = @intCast(case_block.instructions.items.len),
  12520                 }));
  12521                 cases_extra.appendAssumeCapacity(@intFromEnum(item));
  12522                 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12523             }
  12524 
  12525             continue;
  12526         }
  12527 
  12528         cases_len += 1;
  12529 
  12530         const analyze_body = if (union_originally)
  12531             for (items) |item| {
  12532                 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  12533                 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
  12534                 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
  12535             } else false
  12536         else
  12537             true;
  12538 
  12539         const prong_hint: std.builtin.BranchHint = if (err_set and
  12540             try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
  12541         h: {
  12542             // nothing to do here. weight against error branch
  12543             break :h .unlikely;
  12544         } else if (analyze_body) h: {
  12545             break :h try spa.analyzeProngRuntime(
  12546                 &case_block,
  12547                 .normal,
  12548                 body,
  12549                 info.capture,
  12550                 child_block.src(.{ .switch_capture = .{
  12551                     .switch_node_offset = switch_node_offset,
  12552                     .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12553                 } }),
  12554                 items,
  12555                 .none,
  12556                 false,
  12557             );
  12558         } else h: {
  12559             _ = try case_block.addNoOp(.unreach);
  12560             break :h .none;
  12561         };
  12562 
  12563         try branch_hints.append(gpa, prong_hint);
  12564 
  12565         try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12566             items.len + ranges.len * 2 +
  12567             case_block.instructions.items.len);
  12568         cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12569             .items_len = @intCast(items.len),
  12570             .ranges_len = @intCast(ranges.len),
  12571             .body_len = @intCast(case_block.instructions.items.len),
  12572         }));
  12573 
  12574         for (items) |item| {
  12575             cases_extra.appendAssumeCapacity(@intFromEnum(item));
  12576         }
  12577         for (ranges) |range| {
  12578             cases_extra.appendSliceAssumeCapacity(&.{
  12579                 @intFromEnum(range[0]),
  12580                 @intFromEnum(range[1]),
  12581             });
  12582         }
  12583 
  12584         cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12585     }
  12586 
  12587     const else_body: []const Air.Inst.Index = if (else_prong.body.len != 0 or case_block.wantSafety()) else_body: {
  12588         var emit_bb = false;
  12589         // If this is true we must have a 'true' else prong and not an underscore because
  12590         // underscore prongs can never be inlined. We've already checked for this.
  12591         if (else_prong.is_inline) switch (operand_ty.zigTypeTag(zcu)) {
  12592             .@"enum" => {
  12593                 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
  12594                     return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
  12595                         operand_ty.fmt(pt),
  12596                     });
  12597                 }
  12598                 for (seen_enum_fields, 0..) |f, i| {
  12599                     if (f != null) continue;
  12600                     cases_len += 1;
  12601 
  12602                     const item_val = try pt.enumValueFieldIndex(operand_ty, @intCast(i));
  12603                     const item_ref = Air.internedToRef(item_val.toIntern());
  12604 
  12605                     case_block.instructions.shrinkRetainingCapacity(0);
  12606                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12607 
  12608                     const analyze_body = if (union_originally) blk: {
  12609                         const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
  12610                         break :blk field_ty.zigTypeTag(zcu) != .noreturn;
  12611                     } else true;
  12612 
  12613                     if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
  12614                     emit_bb = true;
  12615 
  12616                     const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
  12617                         break :h try spa.analyzeProngRuntime(
  12618                             &case_block,
  12619                             .special,
  12620                             else_prong.body,
  12621                             else_prong.capture,
  12622                             child_block.src(.{ .switch_capture = .{
  12623                                 .switch_node_offset = switch_node_offset,
  12624                                 .case_idx = .special_else,
  12625                             } }),
  12626                             &.{item_ref},
  12627                             item_ref,
  12628                             else_prong.has_tag_capture,
  12629                         );
  12630                     } else h: {
  12631                         _ = try case_block.addNoOp(.unreach);
  12632                         break :h .none;
  12633                     };
  12634                     try branch_hints.append(gpa, prong_hint);
  12635 
  12636                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12637                         1 + // `item`, no ranges
  12638                         case_block.instructions.items.len);
  12639                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12640                         .items_len = 1,
  12641                         .ranges_len = 0,
  12642                         .body_len = @intCast(case_block.instructions.items.len),
  12643                     }));
  12644                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12645                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12646                 }
  12647             },
  12648             .error_set => {
  12649                 if (operand_ty.isAnyError(zcu)) {
  12650                     return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
  12651                         operand_ty.fmt(pt),
  12652                     });
  12653                 }
  12654                 const error_names = operand_ty.errorSetNames(zcu);
  12655                 for (0..error_names.len) |name_index| {
  12656                     const error_name = error_names.get(ip)[name_index];
  12657                     if (seen_errors.contains(error_name)) continue;
  12658                     cases_len += 1;
  12659 
  12660                     const item_val = try pt.intern(.{ .err = .{
  12661                         .ty = operand_ty.toIntern(),
  12662                         .name = error_name,
  12663                     } });
  12664                     const item_ref = Air.internedToRef(item_val);
  12665 
  12666                     case_block.instructions.shrinkRetainingCapacity(0);
  12667                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12668 
  12669                     if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
  12670                     emit_bb = true;
  12671 
  12672                     const prong_hint = try spa.analyzeProngRuntime(
  12673                         &case_block,
  12674                         .special,
  12675                         else_prong.body,
  12676                         else_prong.capture,
  12677                         child_block.src(.{ .switch_capture = .{
  12678                             .switch_node_offset = switch_node_offset,
  12679                             .case_idx = .special_else,
  12680                         } }),
  12681                         &.{item_ref},
  12682                         item_ref,
  12683                         else_prong.has_tag_capture,
  12684                     );
  12685                     try branch_hints.append(gpa, prong_hint);
  12686 
  12687                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12688                         1 + // `item`, no ranges
  12689                         case_block.instructions.items.len);
  12690                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12691                         .items_len = 1,
  12692                         .ranges_len = 0,
  12693                         .body_len = @intCast(case_block.instructions.items.len),
  12694                     }));
  12695                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12696                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12697                 }
  12698             },
  12699             .int => {
  12700                 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set);
  12701                 while (try it.next()) |cur| {
  12702                     cases_len += 1;
  12703 
  12704                     const item_ref = Air.internedToRef(cur);
  12705 
  12706                     case_block.instructions.shrinkRetainingCapacity(0);
  12707                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12708 
  12709                     if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
  12710                     emit_bb = true;
  12711 
  12712                     const prong_hint = try spa.analyzeProngRuntime(
  12713                         &case_block,
  12714                         .special,
  12715                         else_prong.body,
  12716                         else_prong.capture,
  12717                         child_block.src(.{ .switch_capture = .{
  12718                             .switch_node_offset = switch_node_offset,
  12719                             .case_idx = .special_else,
  12720                         } }),
  12721                         &.{item_ref},
  12722                         item_ref,
  12723                         else_prong.has_tag_capture,
  12724                     );
  12725                     try branch_hints.append(gpa, prong_hint);
  12726 
  12727                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12728                         1 + // `item`, no ranges
  12729                         case_block.instructions.items.len);
  12730                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12731                         .items_len = 1,
  12732                         .ranges_len = 0,
  12733                         .body_len = @intCast(case_block.instructions.items.len),
  12734                     }));
  12735                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12736                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12737                 }
  12738             },
  12739             .bool => {
  12740                 if (true_count == 0) {
  12741                     cases_len += 1;
  12742 
  12743                     case_block.instructions.shrinkRetainingCapacity(0);
  12744                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12745 
  12746                     if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
  12747                     emit_bb = true;
  12748 
  12749                     const prong_hint = try spa.analyzeProngRuntime(
  12750                         &case_block,
  12751                         .special,
  12752                         else_prong.body,
  12753                         else_prong.capture,
  12754                         child_block.src(.{ .switch_capture = .{
  12755                             .switch_node_offset = switch_node_offset,
  12756                             .case_idx = .special_else,
  12757                         } }),
  12758                         &.{.bool_true},
  12759                         .bool_true,
  12760                         else_prong.has_tag_capture,
  12761                     );
  12762                     try branch_hints.append(gpa, prong_hint);
  12763 
  12764                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12765                         1 + // `item`, no ranges
  12766                         case_block.instructions.items.len);
  12767                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12768                         .items_len = 1,
  12769                         .ranges_len = 0,
  12770                         .body_len = @intCast(case_block.instructions.items.len),
  12771                     }));
  12772                     cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true));
  12773                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12774                 }
  12775                 if (false_count == 0) {
  12776                     cases_len += 1;
  12777 
  12778                     case_block.instructions.shrinkRetainingCapacity(0);
  12779                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12780 
  12781                     if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
  12782                     emit_bb = true;
  12783 
  12784                     const prong_hint = try spa.analyzeProngRuntime(
  12785                         &case_block,
  12786                         .special,
  12787                         else_prong.body,
  12788                         else_prong.capture,
  12789                         child_block.src(.{ .switch_capture = .{
  12790                             .switch_node_offset = switch_node_offset,
  12791                             .case_idx = .special_else,
  12792                         } }),
  12793                         &.{.bool_false},
  12794                         .bool_false,
  12795                         else_prong.has_tag_capture,
  12796                     );
  12797                     try branch_hints.append(gpa, prong_hint);
  12798 
  12799                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12800                         1 + // `item`, no ranges
  12801                         case_block.instructions.items.len);
  12802                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12803                         .items_len = 1,
  12804                         .ranges_len = 0,
  12805                         .body_len = @intCast(case_block.instructions.items.len),
  12806                     }));
  12807                     cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false));
  12808                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12809                 }
  12810             },
  12811             else => return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
  12812                 operand_ty.fmt(pt),
  12813             }),
  12814         };
  12815 
  12816         case_block.instructions.shrinkRetainingCapacity(0);
  12817         case_block.error_return_trace_index = child_block.error_return_trace_index;
  12818 
  12819         if (zcu.backendSupportsFeature(.is_named_enum_value) and
  12820             else_prong.body.len != 0 and block.wantSafety() and
  12821             operand_ty.zigTypeTag(zcu) == .@"enum" and
  12822             (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))
  12823         {
  12824             try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
  12825             const ok = try case_block.addUnOp(.is_named_enum_value, operand);
  12826             try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
  12827         }
  12828 
  12829         const else_src_idx: LazySrcLoc.Offset.SwitchCaseIndex = if (else_prong_is_underscore)
  12830             .special_under
  12831         else
  12832             .special_else;
  12833 
  12834         const analyze_body = if (union_originally and !else_prong.is_inline)
  12835             for (seen_enum_fields, 0..) |seen_field, index| {
  12836                 if (seen_field != null) continue;
  12837                 const union_obj = zcu.typeToUnion(maybe_union_ty).?;
  12838                 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]);
  12839                 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
  12840             } else false
  12841         else
  12842             true;
  12843         const else_hint: std.builtin.BranchHint = if (else_prong.body.len != 0 and err_set and
  12844             try sema.maybeErrorUnwrap(&case_block, else_prong.body, operand, operand_src, allow_err_code_unwrap))
  12845         h: {
  12846             // nothing to do here. weight against error branch
  12847             break :h .unlikely;
  12848         } else if (else_prong.body.len != 0 and analyze_body and !else_prong.is_inline) h: {
  12849             break :h try spa.analyzeProngRuntime(
  12850                 &case_block,
  12851                 .special,
  12852                 else_prong.body,
  12853                 else_prong.capture,
  12854                 child_block.src(.{ .switch_capture = .{
  12855                     .switch_node_offset = switch_node_offset,
  12856                     .case_idx = else_src_idx,
  12857                 } }),
  12858                 undefined, // case_vals may be undefined for special prongs
  12859                 .none,
  12860                 false,
  12861             );
  12862         } else h: {
  12863             // We still need a terminator in this block, but we have proven
  12864             // that it is unreachable.
  12865             if (case_block.wantSafety()) {
  12866                 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
  12867                 try sema.safetyPanic(&case_block, src, .corrupt_switch);
  12868             } else {
  12869                 _ = try case_block.addNoOp(.unreach);
  12870             }
  12871             // Safety check / unreachable branches are cold.
  12872             break :h .cold;
  12873         };
  12874 
  12875         try branch_hints.append(gpa, else_hint);
  12876         break :else_body case_block.instructions.items;
  12877     } else else_body: {
  12878         try branch_hints.append(gpa, .none);
  12879         break :else_body &.{};
  12880     };
  12881 
  12882     assert(branch_hints.items.len == cases_len + 1);
  12883 
  12884     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
  12885         cases_extra.items.len + else_body.len +
  12886         (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints
  12887 
  12888     const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
  12889         .cases_len = @intCast(cases_len),
  12890         .else_body_len = @intCast(else_body.len),
  12891     });
  12892 
  12893     {
  12894         // Add branch hints.
  12895         var cur_bag: u32 = 0;
  12896         for (branch_hints.items, 0..) |hint, idx| {
  12897             const idx_in_bag = idx % 10;
  12898             cur_bag |= @as(u32, @intFromEnum(hint)) << @intCast(idx_in_bag * 3);
  12899             if (idx_in_bag == 9) {
  12900                 sema.air_extra.appendAssumeCapacity(cur_bag);
  12901                 cur_bag = 0;
  12902             }
  12903         }
  12904         if (branch_hints.items.len % 10 != 0) {
  12905             sema.air_extra.appendAssumeCapacity(cur_bag);
  12906         }
  12907     }
  12908     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));
  12909     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_body));
  12910 
  12911     const has_any_continues = spa.operand == .loop and child_block.label.?.merges.extra_insts.items.len > 0;
  12912 
  12913     return try child_block.addInst(.{
  12914         .tag = if (has_any_continues) .loop_switch_br else .switch_br,
  12915         .data = .{ .pl_op = .{
  12916             .operand = operand,
  12917             .payload = payload_index,
  12918         } },
  12919     });
  12920 }
  12921 
  12922 fn resolveSwitchComptimeLoop(
  12923     sema: *Sema,
  12924     init_spa: SwitchProngAnalysis,
  12925     child_block: *Block,
  12926     maybe_ptr_operand_ty: Type,
  12927     cond_ty: Type,
  12928     init_cond_val: Value,
  12929     switch_node_offset: std.zig.Ast.Node.Offset,
  12930     special_members_only: ?SpecialProng,
  12931     special_generic: SpecialProng,
  12932     special_generic_is_under: bool,
  12933     case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
  12934     scalar_cases_len: u32,
  12935     multi_cases_len: u32,
  12936     err_set: bool,
  12937     empty_enum: bool,
  12938     operand_is_ref: bool,
  12939 ) CompileError!Air.Inst.Ref {
  12940     var spa = init_spa;
  12941     var cond_val = init_cond_val;
  12942 
  12943     while (true) {
  12944         if (resolveSwitchComptime(
  12945             sema,
  12946             spa,
  12947             child_block,
  12948             spa.operand.simple.cond,
  12949             cond_val,
  12950             cond_ty,
  12951             switch_node_offset,
  12952             special_members_only,
  12953             special_generic,
  12954             special_generic_is_under,
  12955             case_vals,
  12956             scalar_cases_len,
  12957             multi_cases_len,
  12958             err_set,
  12959             empty_enum,
  12960         )) |result| {
  12961             return result;
  12962         } else |err| switch (err) {
  12963             error.ComptimeBreak => {
  12964                 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst));
  12965                 if (break_inst.tag != .switch_continue) return error.ComptimeBreak;
  12966                 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data;
  12967                 if (extra.block_inst != spa.switch_block_inst) return error.ComptimeBreak;
  12968                 // This is a `switch_continue` targeting this block. Change the operand and start over.
  12969                 const src = child_block.nodeOffset(extra.operand_src_node.unwrap().?);
  12970                 const new_operand_uncoerced = try sema.resolveInst(break_inst.data.@"break".operand);
  12971                 const new_operand = try sema.coerce(child_block, maybe_ptr_operand_ty, new_operand_uncoerced, src);
  12972 
  12973                 try sema.emitBackwardBranch(child_block, src);
  12974 
  12975                 const val, const ref = if (operand_is_ref)
  12976                     .{ try sema.analyzeLoad(child_block, src, new_operand, src), new_operand }
  12977                 else
  12978                     .{ new_operand, undefined };
  12979 
  12980                 const cond_ref = try sema.switchCond(child_block, src, val);
  12981 
  12982                 cond_val = try sema.resolveConstDefinedValue(child_block, src, cond_ref, null);
  12983                 spa.operand = .{ .simple = .{
  12984                     .by_val = val,
  12985                     .by_ref = ref,
  12986                     .cond = cond_ref,
  12987                 } };
  12988             },
  12989             else => |e| return e,
  12990         }
  12991     }
  12992 }
  12993 
  12994 fn resolveSwitchComptime(
  12995     sema: *Sema,
  12996     spa: SwitchProngAnalysis,
  12997     child_block: *Block,
  12998     cond_operand: Air.Inst.Ref,
  12999     operand_val: Value,
  13000     operand_ty: Type,
  13001     switch_node_offset: std.zig.Ast.Node.Offset,
  13002     special_members_only: ?SpecialProng,
  13003     special_generic: SpecialProng,
  13004     special_generic_is_under: bool,
  13005     case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
  13006     scalar_cases_len: u32,
  13007     multi_cases_len: u32,
  13008     err_set: bool,
  13009     empty_enum: bool,
  13010 ) CompileError!Air.Inst.Ref {
  13011     const zcu = sema.pt.zcu;
  13012     const merges = &child_block.label.?.merges;
  13013     const resolved_operand_val = try sema.resolveLazyValue(operand_val);
  13014 
  13015     var extra_index: usize = special_generic.end;
  13016     {
  13017         var scalar_i: usize = 0;
  13018         while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  13019             extra_index += 1;
  13020             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  13021             extra_index += 1;
  13022             const body = sema.code.bodySlice(extra_index, info.body_len);
  13023             extra_index += info.body_len;
  13024 
  13025             const item = case_vals.items[scalar_i];
  13026             const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  13027             if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) {
  13028                 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
  13029                 return spa.resolveProngComptime(
  13030                     child_block,
  13031                     .normal,
  13032                     body,
  13033                     info.capture,
  13034                     child_block.src(.{ .switch_capture = .{
  13035                         .switch_node_offset = switch_node_offset,
  13036                         .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  13037                     } }),
  13038                     &.{item},
  13039                     if (info.is_inline) cond_operand else .none,
  13040                     info.has_tag_capture,
  13041                     merges,
  13042                 );
  13043             }
  13044         }
  13045     }
  13046     {
  13047         var multi_i: usize = 0;
  13048         var case_val_idx: usize = scalar_cases_len;
  13049         while (multi_i < multi_cases_len) : (multi_i += 1) {
  13050             const items_len = sema.code.extra[extra_index];
  13051             extra_index += 1;
  13052             const ranges_len = sema.code.extra[extra_index];
  13053             extra_index += 1;
  13054             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  13055             extra_index += 1 + items_len;
  13056             const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len);
  13057 
  13058             const items = case_vals.items[case_val_idx..][0..items_len];
  13059             case_val_idx += items_len;
  13060 
  13061             for (items) |item| {
  13062                 // Validation above ensured these will succeed.
  13063                 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  13064                 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) {
  13065                     if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
  13066                     return spa.resolveProngComptime(
  13067                         child_block,
  13068                         .normal,
  13069                         body,
  13070                         info.capture,
  13071                         child_block.src(.{ .switch_capture = .{
  13072                             .switch_node_offset = switch_node_offset,
  13073                             .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  13074                         } }),
  13075                         items,
  13076                         if (info.is_inline) cond_operand else .none,
  13077                         info.has_tag_capture,
  13078                         merges,
  13079                     );
  13080                 }
  13081             }
  13082 
  13083             var range_i: usize = 0;
  13084             while (range_i < ranges_len) : (range_i += 1) {
  13085                 const range_items = case_vals.items[case_val_idx..][0..2];
  13086                 extra_index += 2;
  13087                 case_val_idx += 2;
  13088 
  13089                 // Validation above ensured these will succeed.
  13090                 const first_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable;
  13091                 const last_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable;
  13092                 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and
  13093                     (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty)))
  13094                 {
  13095                     if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
  13096                     return spa.resolveProngComptime(
  13097                         child_block,
  13098                         .normal,
  13099                         body,
  13100                         info.capture,
  13101                         child_block.src(.{ .switch_capture = .{
  13102                             .switch_node_offset = switch_node_offset,
  13103                             .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  13104                         } }),
  13105                         undefined, // case_vals may be undefined for ranges
  13106                         if (info.is_inline) cond_operand else .none,
  13107                         info.has_tag_capture,
  13108                         merges,
  13109                     );
  13110                 }
  13111             }
  13112 
  13113             extra_index += info.body_len;
  13114         }
  13115     }
  13116     if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special_generic.body, cond_operand);
  13117     if (empty_enum) {
  13118         return .void_value;
  13119     }
  13120     if (special_members_only) |special| {
  13121         assert(operand_ty.isNonexhaustiveEnum(zcu));
  13122         if (operand_ty.enumTagFieldIndex(operand_val, zcu)) |_| {
  13123             return spa.resolveProngComptime(
  13124                 child_block,
  13125                 .special,
  13126                 special.body,
  13127                 special.capture,
  13128                 child_block.src(.{ .switch_capture = .{
  13129                     .switch_node_offset = switch_node_offset,
  13130                     .case_idx = .special_else,
  13131                 } }),
  13132                 undefined, // case_vals may be undefined for special prongs
  13133                 if (special.is_inline) cond_operand else .none,
  13134                 special.has_tag_capture,
  13135                 merges,
  13136             );
  13137         }
  13138     }
  13139 
  13140     return spa.resolveProngComptime(
  13141         child_block,
  13142         .special,
  13143         special_generic.body,
  13144         special_generic.capture,
  13145         child_block.src(.{ .switch_capture = .{
  13146             .switch_node_offset = switch_node_offset,
  13147             .case_idx = if (special_generic_is_under)
  13148                 .special_under
  13149             else
  13150                 .special_else,
  13151         } }),
  13152         undefined, // case_vals may be undefined for special prongs
  13153         if (special_generic.is_inline) cond_operand else .none,
  13154         special_generic.has_tag_capture,
  13155         merges,
  13156     );
  13157 }
  13158 
  13159 const RangeSetUnhandledIterator = struct {
  13160     pt: Zcu.PerThread,
  13161     cur: ?InternPool.Index,
  13162     max: InternPool.Index,
  13163     range_i: usize,
  13164     ranges: []const RangeSet.Range,
  13165     limbs: []math.big.Limb,
  13166 
  13167     const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128);
  13168 
  13169     fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
  13170         const pt = sema.pt;
  13171         const int_type = pt.zcu.intern_pool.indexToKey(ty.toIntern()).int_type;
  13172         const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits);
  13173         return .{
  13174             .pt = pt,
  13175             .cur = (try ty.minInt(pt, ty)).toIntern(),
  13176             .max = (try ty.maxInt(pt, ty)).toIntern(),
  13177             .range_i = 0,
  13178             .ranges = range_set.ranges.items,
  13179             .limbs = if (needed_limbs > preallocated_limbs)
  13180                 try sema.arena.alloc(math.big.Limb, needed_limbs)
  13181             else
  13182                 &.{},
  13183         };
  13184     }
  13185 
  13186     fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index {
  13187         if (val == it.max) return null;
  13188         const int = it.pt.zcu.intern_pool.indexToKey(val).int;
  13189 
  13190         switch (int.storage) {
  13191             inline .u64, .i64 => |val_int| {
  13192                 const next_int = @addWithOverflow(val_int, 1);
  13193                 if (next_int[1] == 0)
  13194                     return (try it.pt.intValue(.fromInterned(int.ty), next_int[0])).toIntern();
  13195             },
  13196             .big_int => {},
  13197             .lazy_align, .lazy_size => unreachable,
  13198         }
  13199 
  13200         var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  13201         const val_bigint = int.storage.toBigInt(&val_space);
  13202 
  13203         var result_limbs: [preallocated_limbs]math.big.Limb = undefined;
  13204         var result_bigint = math.big.int.Mutable.init(
  13205             if (it.limbs.len > 0) it.limbs else &result_limbs,
  13206             0,
  13207         );
  13208 
  13209         result_bigint.addScalar(val_bigint, 1);
  13210         return (try it.pt.intValue_big(.fromInterned(int.ty), result_bigint.toConst())).toIntern();
  13211     }
  13212 
  13213     fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index {
  13214         var cur = it.cur orelse return null;
  13215         while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) {
  13216             defer it.range_i += 1;
  13217             cur = (try it.addOne(it.ranges[it.range_i].last)) orelse {
  13218                 it.cur = null;
  13219                 return null;
  13220             };
  13221         }
  13222         it.cur = try it.addOne(cur);
  13223         return cur;
  13224     }
  13225 };
  13226 
  13227 const ResolvedSwitchItem = struct {
  13228     ref: Air.Inst.Ref,
  13229     val: InternPool.Index,
  13230 };
  13231 fn resolveSwitchItemVal(
  13232     sema: *Sema,
  13233     block: *Block,
  13234     item_ref: Zir.Inst.Ref,
  13235     /// Coerce `item_ref` to this type.
  13236     coerce_ty: Type,
  13237     item_src: LazySrcLoc,
  13238 ) CompileError!ResolvedSwitchItem {
  13239     const uncoerced_item = try sema.resolveInst(item_ref);
  13240 
  13241     // Constructing a LazySrcLoc is costly because we only have the switch AST node.
  13242     // Only if we know for sure we need to report a compile error do we resolve the
  13243     // full source locations.
  13244 
  13245     const item = try sema.coerce(block, coerce_ty, uncoerced_item, item_src);
  13246 
  13247     const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item, .{ .simple = .switch_item });
  13248 
  13249     const val = try sema.resolveLazyValue(maybe_lazy);
  13250     const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: {
  13251         break :blk Air.internedToRef(val.toIntern());
  13252     } else item;
  13253 
  13254     return .{ .ref = new_item, .val = val.toIntern() };
  13255 }
  13256 
  13257 fn validateErrSetSwitch(
  13258     sema: *Sema,
  13259     block: *Block,
  13260     seen_errors: *SwitchErrorSet,
  13261     case_vals: *std.ArrayListUnmanaged(Air.Inst.Ref),
  13262     operand_ty: Type,
  13263     inst_data: @FieldType(Zir.Inst.Data, "pl_node"),
  13264     scalar_cases_len: u32,
  13265     multi_cases_len: u32,
  13266     else_case: struct { body: []const Zir.Inst.Index, end: usize, src: LazySrcLoc },
  13267     has_else: bool,
  13268 ) CompileError!?Type {
  13269     const gpa = sema.gpa;
  13270     const pt = sema.pt;
  13271     const zcu = pt.zcu;
  13272     const ip = &zcu.intern_pool;
  13273 
  13274     const src_node_offset = inst_data.src_node;
  13275     const src = block.nodeOffset(src_node_offset);
  13276 
  13277     var extra_index: usize = else_case.end;
  13278     {
  13279         var scalar_i: u32 = 0;
  13280         while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  13281             const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  13282             extra_index += 1;
  13283             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  13284             extra_index += 1 + info.body_len;
  13285 
  13286             case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
  13287                 block,
  13288                 seen_errors,
  13289                 item_ref,
  13290                 operand_ty,
  13291                 block.src(.{ .switch_case_item = .{
  13292                     .switch_node_offset = src_node_offset,
  13293                     .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  13294                     .item_idx = .{ .kind = .single, .index = 0 },
  13295                 } }),
  13296             ));
  13297         }
  13298     }
  13299     {
  13300         var multi_i: u32 = 0;
  13301         while (multi_i < multi_cases_len) : (multi_i += 1) {
  13302             const items_len = sema.code.extra[extra_index];
  13303             extra_index += 1;
  13304             const ranges_len = sema.code.extra[extra_index];
  13305             extra_index += 1;
  13306             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  13307             extra_index += 1;
  13308             const items = sema.code.refSlice(extra_index, items_len);
  13309             extra_index += items_len + info.body_len;
  13310 
  13311             try case_vals.ensureUnusedCapacity(gpa, items.len);
  13312             for (items, 0..) |item_ref, item_i| {
  13313                 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
  13314                     block,
  13315                     seen_errors,
  13316                     item_ref,
  13317                     operand_ty,
  13318                     block.src(.{ .switch_case_item = .{
  13319                         .switch_node_offset = src_node_offset,
  13320                         .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  13321                         .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  13322                     } }),
  13323                 ));
  13324             }
  13325 
  13326             try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  13327         }
  13328     }
  13329 
  13330     switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) {
  13331         .anyerror_type => {
  13332             if (!has_else) {
  13333                 return sema.fail(
  13334                     block,
  13335                     src,
  13336                     "else prong required when switching on type 'anyerror'",
  13337                     .{},
  13338                 );
  13339             }
  13340             return .anyerror;
  13341         },
  13342         else => |err_set_ty_index| else_validation: {
  13343             const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
  13344             var maybe_msg: ?*Zcu.ErrorMsg = null;
  13345             errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
  13346 
  13347             for (error_names.get(ip)) |error_name| {
  13348                 if (!seen_errors.contains(error_name) and !has_else) {
  13349                     const msg = maybe_msg orelse blk: {
  13350                         maybe_msg = try sema.errMsg(
  13351                             src,
  13352                             "switch must handle all possibilities",
  13353                             .{},
  13354                         );
  13355                         break :blk maybe_msg.?;
  13356                     };
  13357 
  13358                     try sema.errNote(
  13359                         src,
  13360                         msg,
  13361                         "unhandled error value: 'error.{f}'",
  13362                         .{error_name.fmt(ip)},
  13363                     );
  13364                 }
  13365             }
  13366 
  13367             if (maybe_msg) |msg| {
  13368                 maybe_msg = null;
  13369                 try sema.addDeclaredHereNote(msg, operand_ty);
  13370                 return sema.failWithOwnedErrorMsg(block, msg);
  13371             }
  13372 
  13373             if (has_else and seen_errors.count() == error_names.len) {
  13374                 // In order to enable common patterns for generic code allow simple else bodies
  13375                 // else => unreachable,
  13376                 // else => return,
  13377                 // else => |e| return e,
  13378                 // even if all the possible errors were already handled.
  13379                 const tags = sema.code.instructions.items(.tag);
  13380                 const datas = sema.code.instructions.items(.data);
  13381                 for (else_case.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) {
  13382                     .dbg_stmt,
  13383                     .dbg_var_val,
  13384                     .ret_type,
  13385                     .as_node,
  13386                     .ret_node,
  13387                     .@"unreachable",
  13388                     .@"defer",
  13389                     .defer_err_code,
  13390                     .err_union_code,
  13391                     .ret_err_value_code,
  13392                     .save_err_ret_index,
  13393                     .restore_err_ret_index_unconditional,
  13394                     .restore_err_ret_index_fn_entry,
  13395                     .is_non_err,
  13396                     .ret_is_non_err,
  13397                     .condbr,
  13398                     => {},
  13399                     .extended => switch (datas[@intFromEnum(else_inst)].extended.opcode) {
  13400                         .restore_err_ret_index => {},
  13401                         else => break,
  13402                     },
  13403                     else => break,
  13404                 } else break :else_validation;
  13405 
  13406                 return sema.fail(
  13407                     block,
  13408                     else_case.src,
  13409                     "unreachable else prong; all cases already handled",
  13410                     .{},
  13411                 );
  13412             }
  13413 
  13414             var names: InferredErrorSet.NameMap = .{};
  13415             try names.ensureUnusedCapacity(sema.arena, error_names.len);
  13416             for (error_names.get(ip)) |error_name| {
  13417                 if (seen_errors.contains(error_name)) continue;
  13418 
  13419                 names.putAssumeCapacityNoClobber(error_name, {});
  13420             }
  13421             // No need to keep the hash map metadata correct; here we
  13422             // extract the (sorted) keys only.
  13423             return try pt.errorSetFromUnsortedNames(names.keys());
  13424         },
  13425     }
  13426     return null;
  13427 }
  13428 
  13429 fn validateSwitchRange(
  13430     sema: *Sema,
  13431     block: *Block,
  13432     range_set: *RangeSet,
  13433     first_ref: Zir.Inst.Ref,
  13434     last_ref: Zir.Inst.Ref,
  13435     operand_ty: Type,
  13436     item_src: LazySrcLoc,
  13437 ) CompileError![2]Air.Inst.Ref {
  13438     const first_src: LazySrcLoc = .{
  13439         .base_node_inst = item_src.base_node_inst,
  13440         .offset = .{ .switch_case_item_range_first = item_src.offset.switch_case_item },
  13441     };
  13442     const last_src: LazySrcLoc = .{
  13443         .base_node_inst = item_src.base_node_inst,
  13444         .offset = .{ .switch_case_item_range_last = item_src.offset.switch_case_item },
  13445     };
  13446     const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, first_src);
  13447     const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, last_src);
  13448     if (try Value.fromInterned(first.val).compareAll(.gt, Value.fromInterned(last.val), operand_ty, sema.pt)) {
  13449         return sema.fail(block, item_src, "range start value is greater than the end value", .{});
  13450     }
  13451     const maybe_prev_src = try range_set.add(first.val, last.val, item_src);
  13452     try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13453     return .{ first.ref, last.ref };
  13454 }
  13455 
  13456 fn validateSwitchItemInt(
  13457     sema: *Sema,
  13458     block: *Block,
  13459     range_set: *RangeSet,
  13460     item_ref: Zir.Inst.Ref,
  13461     operand_ty: Type,
  13462     item_src: LazySrcLoc,
  13463 ) CompileError!Air.Inst.Ref {
  13464     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
  13465     const maybe_prev_src = try range_set.add(item.val, item.val, item_src);
  13466     try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13467     return item.ref;
  13468 }
  13469 
  13470 fn validateSwitchItemEnum(
  13471     sema: *Sema,
  13472     block: *Block,
  13473     seen_fields: []?LazySrcLoc,
  13474     range_set: *RangeSet,
  13475     item_ref: Zir.Inst.Ref,
  13476     operand_ty: Type,
  13477     item_src: LazySrcLoc,
  13478 ) CompileError!Air.Inst.Ref {
  13479     const ip = &sema.pt.zcu.intern_pool;
  13480     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
  13481     const int = ip.indexToKey(item.val).enum_tag.int;
  13482     const field_index = ip.loadEnumType(ip.typeOf(item.val)).tagValueIndex(ip, int) orelse {
  13483         const maybe_prev_src = try range_set.add(int, int, item_src);
  13484         try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13485         return item.ref;
  13486     };
  13487     const maybe_prev_src = seen_fields[field_index];
  13488     seen_fields[field_index] = item_src;
  13489     try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13490     return item.ref;
  13491 }
  13492 
  13493 fn validateSwitchItemError(
  13494     sema: *Sema,
  13495     block: *Block,
  13496     seen_errors: *SwitchErrorSet,
  13497     item_ref: Zir.Inst.Ref,
  13498     operand_ty: Type,
  13499     item_src: LazySrcLoc,
  13500 ) CompileError!Air.Inst.Ref {
  13501     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
  13502     const error_name = sema.pt.zcu.intern_pool.indexToKey(item.val).err.name;
  13503     const maybe_prev_src = if (try seen_errors.fetchPut(error_name, item_src)) |prev|
  13504         prev.value
  13505     else
  13506         null;
  13507     try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13508     return item.ref;
  13509 }
  13510 
  13511 fn validateSwitchDupe(
  13512     sema: *Sema,
  13513     block: *Block,
  13514     maybe_prev_src: ?LazySrcLoc,
  13515     item_src: LazySrcLoc,
  13516 ) CompileError!void {
  13517     const prev_item_src = maybe_prev_src orelse return;
  13518     return sema.failWithOwnedErrorMsg(block, msg: {
  13519         const msg = try sema.errMsg(
  13520             item_src,
  13521             "duplicate switch value",
  13522             .{},
  13523         );
  13524         errdefer msg.destroy(sema.gpa);
  13525         try sema.errNote(
  13526             prev_item_src,
  13527             msg,
  13528             "previous value here",
  13529             .{},
  13530         );
  13531         break :msg msg;
  13532     });
  13533 }
  13534 
  13535 fn validateSwitchItemBool(
  13536     sema: *Sema,
  13537     block: *Block,
  13538     true_count: *u8,
  13539     false_count: *u8,
  13540     item_ref: Zir.Inst.Ref,
  13541     item_src: LazySrcLoc,
  13542 ) CompileError!Air.Inst.Ref {
  13543     const item = try sema.resolveSwitchItemVal(block, item_ref, .bool, item_src);
  13544     if (Value.fromInterned(item.val).toBool()) {
  13545         true_count.* += 1;
  13546     } else {
  13547         false_count.* += 1;
  13548     }
  13549     if (true_count.* > 1 or false_count.* > 1) {
  13550         return sema.fail(block, item_src, "duplicate switch value", .{});
  13551     }
  13552     return item.ref;
  13553 }
  13554 
  13555 const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc);
  13556 
  13557 fn validateSwitchItemSparse(
  13558     sema: *Sema,
  13559     block: *Block,
  13560     seen_values: *ValueSrcMap,
  13561     item_ref: Zir.Inst.Ref,
  13562     operand_ty: Type,
  13563     item_src: LazySrcLoc,
  13564 ) CompileError!Air.Inst.Ref {
  13565     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
  13566     const kv = try seen_values.fetchPut(sema.gpa, item.val, item_src) orelse return item.ref;
  13567     try sema.validateSwitchDupe(block, kv.value, item_src);
  13568     unreachable;
  13569 }
  13570 
  13571 fn validateSwitchNoRange(
  13572     sema: *Sema,
  13573     block: *Block,
  13574     ranges_len: u32,
  13575     operand_ty: Type,
  13576     src_node_offset: std.zig.Ast.Node.Offset,
  13577 ) CompileError!void {
  13578     if (ranges_len == 0)
  13579         return;
  13580 
  13581     const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
  13582     const range_src = block.src(.{ .node_offset_switch_range = src_node_offset });
  13583 
  13584     const msg = msg: {
  13585         const msg = try sema.errMsg(
  13586             operand_src,
  13587             "ranges not allowed when switching on type '{f}'",
  13588             .{operand_ty.fmt(sema.pt)},
  13589         );
  13590         errdefer msg.destroy(sema.gpa);
  13591         try sema.errNote(
  13592             range_src,
  13593             msg,
  13594             "range here",
  13595             .{},
  13596         );
  13597         break :msg msg;
  13598     };
  13599     return sema.failWithOwnedErrorMsg(block, msg);
  13600 }
  13601 
  13602 fn maybeErrorUnwrap(
  13603     sema: *Sema,
  13604     block: *Block,
  13605     body: []const Zir.Inst.Index,
  13606     operand: Air.Inst.Ref,
  13607     operand_src: LazySrcLoc,
  13608     allow_err_code_inst: bool,
  13609 ) !bool {
  13610     const pt = sema.pt;
  13611     const zcu = pt.zcu;
  13612 
  13613     const tags = sema.code.instructions.items(.tag);
  13614     for (body) |inst| {
  13615         switch (tags[@intFromEnum(inst)]) {
  13616             .@"unreachable" => if (!block.wantSafety()) return false,
  13617             .err_union_code => if (!allow_err_code_inst) return false,
  13618             .save_err_ret_index,
  13619             .dbg_stmt,
  13620             .str,
  13621             .as_node,
  13622             .panic,
  13623             => {},
  13624             else => return false,
  13625         }
  13626     }
  13627 
  13628     for (body) |inst| {
  13629         const air_inst = switch (tags[@intFromEnum(inst)]) {
  13630             .err_union_code => continue,
  13631             .dbg_stmt => {
  13632                 try sema.zirDbgStmt(block, inst);
  13633                 continue;
  13634             },
  13635             .save_err_ret_index => {
  13636                 try sema.zirSaveErrRetIndex(block, inst);
  13637                 continue;
  13638             },
  13639             .str => try sema.zirStr(inst),
  13640             .as_node => try sema.zirAsNode(block, inst),
  13641             .@"unreachable" => {
  13642                 try safetyPanicUnwrapError(sema, block, operand_src, operand);
  13643                 return true;
  13644             },
  13645             .panic => {
  13646                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  13647                 const msg_inst = try sema.resolveInst(inst_data.operand);
  13648 
  13649                 const panic_fn = try getBuiltin(sema, operand_src, .@"panic.call");
  13650                 const args: [2]Air.Inst.Ref = .{ msg_inst, .null_value };
  13651                 try sema.callBuiltin(block, operand_src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check");
  13652                 return true;
  13653             },
  13654             else => unreachable,
  13655         };
  13656         if (sema.typeOf(air_inst).isNoReturn(zcu))
  13657             return true;
  13658         sema.inst_map.putAssumeCapacity(inst, air_inst);
  13659     }
  13660     unreachable;
  13661 }
  13662 
  13663 fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void {
  13664     const pt = sema.pt;
  13665     const zcu = pt.zcu;
  13666     const index = cond.toIndex() orelse return;
  13667     if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) return;
  13668 
  13669     const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node;
  13670     const err_operand = try sema.resolveInst(err_inst_data.operand);
  13671     const operand_ty = sema.typeOf(err_operand);
  13672     if (operand_ty.zigTypeTag(zcu) == .error_set) {
  13673         try sema.maybeErrorUnwrapComptime(block, body, err_operand);
  13674         return;
  13675     }
  13676     if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| {
  13677         if (!operand_ty.isError(zcu)) return;
  13678         if (val.getErrorName(zcu) == .none) return;
  13679         try sema.maybeErrorUnwrapComptime(block, body, err_operand);
  13680     }
  13681 }
  13682 
  13683 fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void {
  13684     const tags = sema.code.instructions.items(.tag);
  13685     const inst = for (body) |inst| {
  13686         switch (tags[@intFromEnum(inst)]) {
  13687             .dbg_stmt,
  13688             .save_err_ret_index,
  13689             => {},
  13690             .@"unreachable" => break inst,
  13691             else => return,
  13692         }
  13693     } else return;
  13694     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
  13695     const src = block.nodeOffset(inst_data.src_node);
  13696 
  13697     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
  13698         if (val.getErrorName(sema.pt.zcu).unwrap()) |name| {
  13699             return sema.failWithComptimeErrorRetTrace(block, src, name);
  13700         }
  13701     }
  13702 }
  13703 
  13704 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13705     const pt = sema.pt;
  13706     const zcu = pt.zcu;
  13707     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13708     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13709     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  13710     const name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  13711     const ty = try sema.resolveType(block, ty_src, extra.lhs);
  13712     const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name });
  13713     try ty.resolveFields(pt);
  13714     const ip = &zcu.intern_pool;
  13715 
  13716     const has_field = hf: {
  13717         switch (ip.indexToKey(ty.toIntern())) {
  13718             .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  13719                 .slice => {
  13720                     if (field_name.eqlSlice("ptr", ip)) break :hf true;
  13721                     if (field_name.eqlSlice("len", ip)) break :hf true;
  13722                     break :hf false;
  13723                 },
  13724                 else => {},
  13725             },
  13726             .tuple_type => |tuple| {
  13727                 const field_index = field_name.toUnsigned(ip) orelse break :hf false;
  13728                 break :hf field_index < tuple.types.len;
  13729             },
  13730             .struct_type => {
  13731                 break :hf ip.loadStructType(ty.toIntern()).nameIndex(ip, field_name) != null;
  13732             },
  13733             .union_type => {
  13734                 const union_type = ip.loadUnionType(ty.toIntern());
  13735                 break :hf union_type.loadTagType(ip).nameIndex(ip, field_name) != null;
  13736             },
  13737             .enum_type => {
  13738                 break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null;
  13739             },
  13740             .array_type => break :hf field_name.eqlSlice("len", ip),
  13741             else => {},
  13742         }
  13743         return sema.fail(block, ty_src, "type '{f}' does not support '@hasField'", .{
  13744             ty.fmt(pt),
  13745         });
  13746     };
  13747     return if (has_field) .bool_true else .bool_false;
  13748 }
  13749 
  13750 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13751     const pt = sema.pt;
  13752     const zcu = pt.zcu;
  13753     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13754     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13755     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  13756     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  13757     const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
  13758     const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{ .simple = .decl_name });
  13759 
  13760     try sema.checkNamespaceType(block, lhs_src, container_type);
  13761 
  13762     const namespace = container_type.getNamespace(zcu).unwrap() orelse return .bool_false;
  13763     if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| {
  13764         if (lookup.accessible) {
  13765             return .bool_true;
  13766         }
  13767     }
  13768     return .bool_false;
  13769 }
  13770 
  13771 fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13772     const tracy = trace(@src());
  13773     defer tracy.end();
  13774 
  13775     const pt = sema.pt;
  13776     const zcu = pt.zcu;
  13777     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
  13778     const extra = sema.code.extraData(Zir.Inst.Import, inst_data.payload_index).data;
  13779     const operand_src = block.tokenOffset(inst_data.src_tok);
  13780     const operand = sema.code.nullTerminatedString(extra.path);
  13781 
  13782     const result = pt.doImport(block.getFileScope(zcu), operand) catch |err| switch (err) {
  13783         error.ModuleNotFound => return sema.fail(block, operand_src, "no module named '{s}' available within module '{s}'", .{
  13784             operand, block.getFileScope(zcu).mod.?.fully_qualified_name,
  13785         }),
  13786         error.IllegalZigImport => unreachable, // caught before semantic analysis
  13787         error.OutOfMemory => |e| return e,
  13788     };
  13789     const file_index = result.file;
  13790     const file = zcu.fileByIndex(file_index);
  13791     switch (file.getMode()) {
  13792         .zig => {
  13793             try pt.ensureFileAnalyzed(file_index);
  13794             const ty = zcu.fileRootType(file_index);
  13795             try sema.declareDependency(.{ .interned = ty });
  13796             try sema.addTypeReferenceEntry(operand_src, ty);
  13797             return Air.internedToRef(ty);
  13798         },
  13799         .zon => {
  13800             const res_ty: InternPool.Index = b: {
  13801                 if (extra.res_ty == .none) break :b .none;
  13802                 const res_ty_inst = try sema.resolveInst(extra.res_ty);
  13803                 const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst);
  13804                 if (res_ty.isGenericPoison()) break :b .none;
  13805                 break :b res_ty.toIntern();
  13806             };
  13807 
  13808             try sema.declareDependency(.{ .zon_file = file_index });
  13809             const interned = try LowerZon.run(
  13810                 sema,
  13811                 file,
  13812                 file_index,
  13813                 res_ty,
  13814                 operand_src,
  13815                 block,
  13816             );
  13817             return Air.internedToRef(interned);
  13818         },
  13819     }
  13820 }
  13821 
  13822 fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13823     const tracy = trace(@src());
  13824     defer tracy.end();
  13825 
  13826     const pt = sema.pt;
  13827     const zcu = pt.zcu;
  13828 
  13829     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  13830     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  13831     const name = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .operand_embedFile });
  13832 
  13833     if (name.len == 0) {
  13834         return sema.fail(block, operand_src, "file path name cannot be empty", .{});
  13835     }
  13836 
  13837     const ef_idx = pt.embedFile(block.getFileScope(zcu), name) catch |err| switch (err) {
  13838         error.ImportOutsideModulePath => {
  13839             return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name});
  13840         },
  13841         error.CurrentWorkingDirectoryUnlinked => {
  13842             // TODO: this should be some kind of retryable failure, in case the cwd is put back
  13843             return sema.fail(block, operand_src, "unable to resolve '{s}': working directory has been unlinked", .{name});
  13844         },
  13845         error.OutOfMemory => |e| return e,
  13846     };
  13847     try sema.declareDependency(.{ .embed_file = ef_idx });
  13848 
  13849     const result = ef_idx.get(zcu);
  13850     if (result.val == .none) {
  13851         return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(result.err.?) });
  13852     }
  13853 
  13854     return Air.internedToRef(result.val);
  13855 }
  13856 
  13857 fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13858     const pt = sema.pt;
  13859     const zcu = pt.zcu;
  13860     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
  13861     const name = try zcu.intern_pool.getOrPutString(
  13862         sema.gpa,
  13863         pt.tid,
  13864         inst_data.get(sema.code),
  13865         .no_embedded_nulls,
  13866     );
  13867     _ = try pt.getErrorValue(name);
  13868     const error_set_type = try pt.singleErrorSetType(name);
  13869     return Air.internedToRef((try pt.intern(.{ .err = .{
  13870         .ty = error_set_type.toIntern(),
  13871         .name = name,
  13872     } })));
  13873 }
  13874 
  13875 fn zirShl(
  13876     sema: *Sema,
  13877     block: *Block,
  13878     inst: Zir.Inst.Index,
  13879     air_tag: Air.Inst.Tag,
  13880 ) CompileError!Air.Inst.Ref {
  13881     const tracy = trace(@src());
  13882     defer tracy.end();
  13883 
  13884     const pt = sema.pt;
  13885     const zcu = pt.zcu;
  13886     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13887     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13888     const lhs = try sema.resolveInst(extra.lhs);
  13889     const rhs = try sema.resolveInst(extra.rhs);
  13890     const lhs_ty = sema.typeOf(lhs);
  13891     const rhs_ty = sema.typeOf(rhs);
  13892 
  13893     const src = block.nodeOffset(inst_data.src_node);
  13894     const lhs_src, const rhs_src = switch (air_tag) {
  13895         .shl, .shl_sat => .{
  13896             block.src(.{ .node_offset_bin_lhs = inst_data.src_node }),
  13897             block.src(.{ .node_offset_bin_rhs = inst_data.src_node }),
  13898         },
  13899         .shl_exact => .{
  13900             block.builtinCallArgSrc(inst_data.src_node, 0),
  13901             block.builtinCallArgSrc(inst_data.src_node, 1),
  13902         },
  13903         else => unreachable,
  13904     };
  13905 
  13906     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13907 
  13908     const scalar_ty = lhs_ty.scalarType(zcu);
  13909     const scalar_rhs_ty = rhs_ty.scalarType(zcu);
  13910 
  13911     // AstGen currently forces the rhs of `<<` to coerce to the correct type before the `.shl` instruction, so
  13912     // we already know `scalar_rhs_ty` is valid for `.shl` -- we only need to validate for `.shl_sat`.
  13913     if (air_tag == .shl_sat) _ = try sema.checkIntType(block, rhs_src, scalar_rhs_ty);
  13914 
  13915     const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs);
  13916     const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs);
  13917 
  13918     const runtime_src = rs: {
  13919         if (maybe_rhs_val) |rhs_val| {
  13920             if (maybe_lhs_val) |lhs_val| {
  13921                 return .fromValue(try arith.shl(sema, block, lhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, switch (air_tag) {
  13922                     .shl => .shl,
  13923                     .shl_sat => .shl_sat,
  13924                     .shl_exact => .shl_exact,
  13925                     else => unreachable,
  13926                 }));
  13927             }
  13928             if (rhs_val.isUndef(zcu)) switch (air_tag) {
  13929                 .shl_sat => return pt.undefRef(lhs_ty),
  13930                 .shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, null),
  13931                 else => unreachable,
  13932             };
  13933             const bits = scalar_ty.intInfo(zcu).bits;
  13934             switch (rhs_ty.zigTypeTag(zcu)) {
  13935                 .int, .comptime_int => {
  13936                     switch (try rhs_val.orderAgainstZeroSema(pt)) {
  13937                         .gt => {
  13938                             if (air_tag != .shl_sat) {
  13939                                 var rhs_space: Value.BigIntSpace = undefined;
  13940                                 const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt);
  13941                                 if (rhs_bigint.orderAgainstScalar(bits) != .lt) {
  13942                                     return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null);
  13943                                 }
  13944                             }
  13945                         },
  13946                         .eq => return lhs,
  13947                         .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null),
  13948                     }
  13949                 },
  13950                 .vector => {
  13951                     var any_positive: bool = false;
  13952                     for (0..rhs_ty.vectorLen(zcu)) |elem_idx| {
  13953                         const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
  13954                         if (rhs_elem.isUndef(zcu)) switch (air_tag) {
  13955                             .shl_sat => continue,
  13956                             .shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, elem_idx),
  13957                             else => unreachable,
  13958                         };
  13959                         switch (try rhs_elem.orderAgainstZeroSema(pt)) {
  13960                             .gt => {
  13961                                 if (air_tag != .shl_sat) {
  13962                                     var rhs_elem_space: Value.BigIntSpace = undefined;
  13963                                     const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt);
  13964                                     if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) {
  13965                                         return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx);
  13966                                     }
  13967                                 }
  13968                                 any_positive = true;
  13969                             },
  13970                             .eq => {},
  13971                             .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx),
  13972                         }
  13973                     }
  13974                     if (!any_positive) return lhs;
  13975                 },
  13976                 else => unreachable,
  13977             }
  13978             break :rs lhs_src;
  13979         } else {
  13980             if (air_tag == .shl_sat and scalar_rhs_ty.isSignedInt(zcu)) {
  13981                 return sema.fail(block, rhs_src, "shift by signed type '{f}'", .{rhs_ty.fmt(pt)});
  13982             }
  13983             if (scalar_ty.toIntern() == .comptime_int_type) {
  13984                 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
  13985             }
  13986             if (maybe_lhs_val) |lhs_val| {
  13987                 switch (air_tag) {
  13988                     .shl_sat => if (lhs_val.isUndef(zcu)) return pt.undefRef(lhs_ty),
  13989                     .shl, .shl_exact => try sema.checkAllScalarsDefined(block, lhs_src, lhs_val),
  13990                     else => unreachable,
  13991                 }
  13992                 if (try lhs_val.compareAllWithZeroSema(.eq, pt)) return lhs;
  13993             }
  13994         }
  13995         break :rs rhs_src;
  13996     };
  13997     const rt_rhs: Air.Inst.Ref = switch (air_tag) {
  13998         else => unreachable,
  13999         .shl, .shl_exact => rhs,
  14000         // The backend can handle a large runtime rhs better than we can, but
  14001         // we can limit a large comptime rhs better here. This also has the
  14002         // necessary side effect of preventing rhs from being a `comptime_int`.
  14003         .shl_sat => if (maybe_rhs_val) |rhs_val| .fromValue(rt_rhs: {
  14004             const bit_count = scalar_ty.intInfo(zcu).bits;
  14005             const rt_rhs_scalar_ty = try pt.smallestUnsignedInt(bit_count);
  14006             if (!rhs_ty.isVector(zcu)) break :rt_rhs try pt.intValue(
  14007                 rt_rhs_scalar_ty,
  14008                 @min(try rhs_val.getUnsignedIntSema(pt) orelse bit_count, bit_count),
  14009             );
  14010             const rhs_len = rhs_ty.vectorLen(zcu);
  14011             const rhs_elems = try sema.arena.alloc(InternPool.Index, rhs_len);
  14012             for (rhs_elems, 0..) |*rhs_elem, i| rhs_elem.* = (try pt.intValue(
  14013                 rt_rhs_scalar_ty,
  14014                 @min(try (try rhs_val.elemValue(pt, i)).getUnsignedIntSema(pt) orelse bit_count, bit_count),
  14015             )).toIntern();
  14016             break :rt_rhs try pt.aggregateValue(try pt.vectorType(.{
  14017                 .len = rhs_len,
  14018                 .child = rt_rhs_scalar_ty.toIntern(),
  14019             }), rhs_elems);
  14020         }) else rhs,
  14021     };
  14022 
  14023     try sema.requireRuntimeBlock(block, src, runtime_src);
  14024     if (block.wantSafety()) {
  14025         const bit_count = scalar_ty.intInfo(zcu).bits;
  14026         if (air_tag != .shl_sat and !std.math.isPowerOfTwo(bit_count)) {
  14027             const bit_count_val = try pt.intValue(scalar_rhs_ty, bit_count);
  14028             const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
  14029                 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern());
  14030                 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
  14031                 break :ok try block.addReduce(lt, .And);
  14032             } else ok: {
  14033                 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern());
  14034                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
  14035             };
  14036             try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
  14037         }
  14038 
  14039         if (air_tag == .shl_exact) {
  14040             const op_ov_tuple_ty = try pt.overflowArithmeticTupleType(lhs_ty);
  14041             const op_ov = try block.addInst(.{
  14042                 .tag = .shl_with_overflow,
  14043                 .data = .{ .ty_pl = .{
  14044                     .ty = .fromIntern(op_ov_tuple_ty.toIntern()),
  14045                     .payload = try sema.addExtra(Air.Bin{
  14046                         .lhs = lhs,
  14047                         .rhs = rhs,
  14048                     }),
  14049                 } },
  14050             });
  14051             const ov_bit = try sema.tupleFieldValByIndex(block, op_ov, 1, op_ov_tuple_ty);
  14052             const any_ov_bit = if (lhs_ty.zigTypeTag(zcu) == .vector)
  14053                 try block.addReduce(ov_bit, .Or)
  14054             else
  14055                 ov_bit;
  14056             const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, .zero_u1);
  14057 
  14058             try sema.addSafetyCheck(block, src, no_ov, .shl_overflow);
  14059             return sema.tupleFieldValByIndex(block, op_ov, 0, op_ov_tuple_ty);
  14060         }
  14061     }
  14062     return block.addBinOp(air_tag, lhs, rt_rhs);
  14063 }
  14064 
  14065 fn zirShr(
  14066     sema: *Sema,
  14067     block: *Block,
  14068     inst: Zir.Inst.Index,
  14069     air_tag: Air.Inst.Tag,
  14070 ) CompileError!Air.Inst.Ref {
  14071     const tracy = trace(@src());
  14072     defer tracy.end();
  14073 
  14074     const pt = sema.pt;
  14075     const zcu = pt.zcu;
  14076     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14077     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14078     const lhs = try sema.resolveInst(extra.lhs);
  14079     const rhs = try sema.resolveInst(extra.rhs);
  14080     const lhs_ty = sema.typeOf(lhs);
  14081     const rhs_ty = sema.typeOf(rhs);
  14082 
  14083     const src = block.nodeOffset(inst_data.src_node);
  14084     const lhs_src = switch (air_tag) {
  14085         .shr => block.src(.{ .node_offset_bin_lhs = inst_data.src_node }),
  14086         .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 0),
  14087         else => unreachable,
  14088     };
  14089     const rhs_src = switch (air_tag) {
  14090         .shr => block.src(.{ .node_offset_bin_rhs = inst_data.src_node }),
  14091         .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 1),
  14092         else => unreachable,
  14093     };
  14094 
  14095     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14096     const scalar_ty = lhs_ty.scalarType(zcu);
  14097 
  14098     const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs);
  14099     const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs);
  14100 
  14101     const runtime_src = rs: {
  14102         if (maybe_rhs_val) |rhs_val| {
  14103             if (maybe_lhs_val) |lhs_val| {
  14104                 return .fromValue(try arith.shr(sema, block, lhs_ty, rhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, switch (air_tag) {
  14105                     .shr => .shr,
  14106                     .shr_exact => .shr_exact,
  14107                     else => unreachable,
  14108                 }));
  14109             }
  14110             if (rhs_val.isUndef(zcu)) {
  14111                 return sema.failWithUseOfUndef(block, rhs_src, null);
  14112             }
  14113             const bits = scalar_ty.intInfo(zcu).bits;
  14114             switch (rhs_ty.zigTypeTag(zcu)) {
  14115                 .int, .comptime_int => {
  14116                     switch (try rhs_val.orderAgainstZeroSema(pt)) {
  14117                         .gt => {
  14118                             var rhs_space: Value.BigIntSpace = undefined;
  14119                             const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt);
  14120                             if (rhs_bigint.orderAgainstScalar(bits) != .lt) {
  14121                                 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null);
  14122                             }
  14123                         },
  14124                         .eq => return lhs,
  14125                         .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null),
  14126                     }
  14127                 },
  14128                 .vector => {
  14129                     var any_positive: bool = false;
  14130                     for (0..rhs_ty.vectorLen(zcu)) |elem_idx| {
  14131                         const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
  14132                         if (rhs_elem.isUndef(zcu)) {
  14133                             return sema.failWithUseOfUndef(block, rhs_src, elem_idx);
  14134                         }
  14135                         switch (try rhs_elem.orderAgainstZeroSema(pt)) {
  14136                             .gt => {
  14137                                 var rhs_elem_space: Value.BigIntSpace = undefined;
  14138                                 const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt);
  14139                                 if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) {
  14140                                     return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx);
  14141                                 }
  14142                                 any_positive = true;
  14143                             },
  14144                             .eq => {},
  14145                             .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx),
  14146                         }
  14147                     }
  14148                     if (!any_positive) return lhs;
  14149                 },
  14150                 else => unreachable,
  14151             }
  14152             break :rs lhs_src;
  14153         } else {
  14154             if (scalar_ty.toIntern() == .comptime_int_type) {
  14155                 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
  14156             }
  14157             if (maybe_lhs_val) |lhs_val| {
  14158                 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  14159                 if (try lhs_val.compareAllWithZeroSema(.eq, pt)) return lhs;
  14160             }
  14161         }
  14162         break :rs rhs_src;
  14163     };
  14164     try sema.requireRuntimeBlock(block, src, runtime_src);
  14165     const result = try block.addBinOp(air_tag, lhs, rhs);
  14166     if (block.wantSafety()) {
  14167         const bit_count = scalar_ty.intInfo(zcu).bits;
  14168         if (!std.math.isPowerOfTwo(bit_count)) {
  14169             const bit_count_val = try pt.intValue(rhs_ty.scalarType(zcu), bit_count);
  14170 
  14171             const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
  14172                 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern());
  14173                 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
  14174                 break :ok try block.addReduce(lt, .And);
  14175             } else ok: {
  14176                 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern());
  14177                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
  14178             };
  14179             try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
  14180         }
  14181 
  14182         if (air_tag == .shr_exact) {
  14183             const back = try block.addBinOp(.shl, result, rhs);
  14184 
  14185             const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
  14186                 const eql = try block.addCmpVector(lhs, back, .eq);
  14187                 break :ok try block.addReduce(eql, .And);
  14188             } else try block.addBinOp(.cmp_eq, lhs, back);
  14189             try sema.addSafetyCheck(block, src, ok, .shr_overflow);
  14190         }
  14191     }
  14192     return result;
  14193 }
  14194 
  14195 fn zirBitwise(
  14196     sema: *Sema,
  14197     block: *Block,
  14198     inst: Zir.Inst.Index,
  14199     air_tag: Air.Inst.Tag,
  14200 ) CompileError!Air.Inst.Ref {
  14201     const tracy = trace(@src());
  14202     defer tracy.end();
  14203 
  14204     const pt = sema.pt;
  14205     const zcu = pt.zcu;
  14206     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14207     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  14208     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  14209     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  14210     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14211     const lhs = try sema.resolveInst(extra.lhs);
  14212     const rhs = try sema.resolveInst(extra.rhs);
  14213     const lhs_ty = sema.typeOf(lhs);
  14214     const rhs_ty = sema.typeOf(rhs);
  14215     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14216 
  14217     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  14218     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
  14219     const scalar_type = resolved_type.scalarType(zcu);
  14220     const scalar_tag = scalar_type.zigTypeTag(zcu);
  14221 
  14222     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14223     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14224 
  14225     const is_int_or_bool = scalar_tag == .int or scalar_tag == .comptime_int or scalar_tag == .bool;
  14226 
  14227     if (!is_int_or_bool) {
  14228         return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(zcu)), @tagName(rhs_ty.zigTypeTag(zcu)) });
  14229     }
  14230 
  14231     const runtime_src = runtime: {
  14232         // TODO: ask the linker what kind of relocations are available, and
  14233         // in some cases emit a Value that means "this decl's address AND'd with this operand".
  14234         if (try sema.resolveValueResolveLazy(casted_lhs)) |lhs_val| {
  14235             if (try sema.resolveValueResolveLazy(casted_rhs)) |rhs_val| {
  14236                 const result_val = switch (air_tag) {
  14237                     // zig fmt: off
  14238                     .bit_and => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"and"),
  14239                     .bit_or  => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"or"),
  14240                     .xor     => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .xor),
  14241                     else     => unreachable,
  14242                     // zig fmt: on
  14243                 };
  14244                 return Air.internedToRef(result_val.toIntern());
  14245             } else {
  14246                 break :runtime rhs_src;
  14247             }
  14248         } else {
  14249             break :runtime lhs_src;
  14250         }
  14251     };
  14252 
  14253     try sema.requireRuntimeBlock(block, src, runtime_src);
  14254     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  14255 }
  14256 
  14257 fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14258     const pt = sema.pt;
  14259     const zcu = pt.zcu;
  14260     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  14261     const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
  14262     const src = block.nodeOffset(inst_data.src_node);
  14263     const operand = try sema.resolveInst(inst_data.operand);
  14264     const operand_ty = sema.typeOf(operand);
  14265     const scalar_ty = operand_ty.scalarType(zcu);
  14266     const scalar_tag = scalar_ty.zigTypeTag(zcu);
  14267 
  14268     if (scalar_tag != .int and scalar_tag != .bool)
  14269         return sema.fail(block, operand_src, "bitwise not operation on type '{f}'", .{operand_ty.fmt(pt)});
  14270 
  14271     return analyzeBitNot(sema, block, operand, src);
  14272 }
  14273 
  14274 fn analyzeBitNot(
  14275     sema: *Sema,
  14276     block: *Block,
  14277     operand: Air.Inst.Ref,
  14278     src: LazySrcLoc,
  14279 ) CompileError!Air.Inst.Ref {
  14280     const operand_ty = sema.typeOf(operand);
  14281     if (try sema.resolveValue(operand)) |operand_val| {
  14282         const result_val = try arith.bitwiseNot(sema, operand_ty, operand_val);
  14283         return Air.internedToRef(result_val.toIntern());
  14284     }
  14285     try sema.requireRuntimeBlock(block, src, null);
  14286     return block.addTyOp(.not, operand_ty, operand);
  14287 }
  14288 
  14289 fn analyzeTupleCat(
  14290     sema: *Sema,
  14291     block: *Block,
  14292     src_node: std.zig.Ast.Node.Offset,
  14293     lhs: Air.Inst.Ref,
  14294     rhs: Air.Inst.Ref,
  14295 ) CompileError!Air.Inst.Ref {
  14296     const pt = sema.pt;
  14297     const zcu = pt.zcu;
  14298     const lhs_ty = sema.typeOf(lhs);
  14299     const rhs_ty = sema.typeOf(rhs);
  14300     const src = block.nodeOffset(src_node);
  14301 
  14302     const lhs_len = lhs_ty.structFieldCount(zcu);
  14303     const rhs_len = rhs_ty.structFieldCount(zcu);
  14304     const dest_fields = lhs_len + rhs_len;
  14305 
  14306     if (dest_fields == 0) {
  14307         return .empty_tuple;
  14308     }
  14309     if (lhs_len == 0) {
  14310         return rhs;
  14311     }
  14312     if (rhs_len == 0) {
  14313         return lhs;
  14314     }
  14315     const final_len = try sema.usizeCast(block, src, dest_fields);
  14316 
  14317     const types = try sema.arena.alloc(InternPool.Index, final_len);
  14318     const values = try sema.arena.alloc(InternPool.Index, final_len);
  14319 
  14320     const opt_runtime_src = rs: {
  14321         var runtime_src: ?LazySrcLoc = null;
  14322         var i: u32 = 0;
  14323         while (i < lhs_len) : (i += 1) {
  14324             types[i] = lhs_ty.fieldType(i, zcu).toIntern();
  14325             const default_val = lhs_ty.structFieldDefaultValue(i, zcu);
  14326             values[i] = default_val.toIntern();
  14327             const operand_src = block.src(.{ .array_cat_lhs = .{
  14328                 .array_cat_offset = src_node,
  14329                 .elem_index = i,
  14330             } });
  14331             if (default_val.toIntern() == .unreachable_value) {
  14332                 runtime_src = operand_src;
  14333                 values[i] = .none;
  14334             }
  14335         }
  14336         i = 0;
  14337         while (i < rhs_len) : (i += 1) {
  14338             types[i + lhs_len] = rhs_ty.fieldType(i, zcu).toIntern();
  14339             const default_val = rhs_ty.structFieldDefaultValue(i, zcu);
  14340             values[i + lhs_len] = default_val.toIntern();
  14341             const operand_src = block.src(.{ .array_cat_rhs = .{
  14342                 .array_cat_offset = src_node,
  14343                 .elem_index = i,
  14344             } });
  14345             if (default_val.toIntern() == .unreachable_value) {
  14346                 runtime_src = operand_src;
  14347                 values[i + lhs_len] = .none;
  14348             }
  14349         }
  14350         break :rs runtime_src;
  14351     };
  14352 
  14353     const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{
  14354         .types = types,
  14355         .values = values,
  14356     }));
  14357 
  14358     const runtime_src = opt_runtime_src orelse {
  14359         const tuple_val = try pt.aggregateValue(tuple_ty, values);
  14360         return Air.internedToRef(tuple_val.toIntern());
  14361     };
  14362 
  14363     try sema.requireRuntimeBlock(block, src, runtime_src);
  14364 
  14365     const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
  14366     var i: u32 = 0;
  14367     while (i < lhs_len) : (i += 1) {
  14368         element_refs[i] = try sema.tupleFieldValByIndex(block, lhs, i, lhs_ty);
  14369     }
  14370     i = 0;
  14371     while (i < rhs_len) : (i += 1) {
  14372         element_refs[i + lhs_len] =
  14373             try sema.tupleFieldValByIndex(block, rhs, i, rhs_ty);
  14374     }
  14375 
  14376     return block.addAggregateInit(tuple_ty, element_refs);
  14377 }
  14378 
  14379 fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14380     const tracy = trace(@src());
  14381     defer tracy.end();
  14382 
  14383     const pt = sema.pt;
  14384     const zcu = pt.zcu;
  14385     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14386     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14387     const lhs = try sema.resolveInst(extra.lhs);
  14388     const rhs = try sema.resolveInst(extra.rhs);
  14389     const lhs_ty = sema.typeOf(lhs);
  14390     const rhs_ty = sema.typeOf(rhs);
  14391     const src = block.nodeOffset(inst_data.src_node);
  14392 
  14393     const lhs_is_tuple = lhs_ty.isTuple(zcu);
  14394     const rhs_is_tuple = rhs_ty.isTuple(zcu);
  14395     if (lhs_is_tuple and rhs_is_tuple) {
  14396         return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs);
  14397     }
  14398 
  14399     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  14400     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  14401 
  14402     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: {
  14403         if (lhs_is_tuple) break :lhs_info undefined;
  14404         return sema.fail(block, lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)});
  14405     };
  14406     const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse {
  14407         assert(!rhs_is_tuple);
  14408         return sema.fail(block, rhs_src, "expected indexable; found '{f}'", .{rhs_ty.fmt(pt)});
  14409     };
  14410 
  14411     const resolved_elem_ty = t: {
  14412         var trash_block = block.makeSubBlock();
  14413         trash_block.comptime_reason = null;
  14414         defer trash_block.instructions.deinit(sema.gpa);
  14415 
  14416         const instructions = [_]Air.Inst.Ref{
  14417             try trash_block.addBitCast(lhs_info.elem_type, .void_value),
  14418             try trash_block.addBitCast(rhs_info.elem_type, .void_value),
  14419         };
  14420         break :t try sema.resolvePeerTypes(block, src, &instructions, .{
  14421             .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14422         });
  14423     };
  14424 
  14425     // When there is a sentinel mismatch, no sentinel on the result.
  14426     // Otherwise, use the sentinel value provided by either operand,
  14427     // coercing it to the peer-resolved element type.
  14428     const res_sent_val: ?Value = s: {
  14429         if (lhs_info.sentinel) |lhs_sent_val| {
  14430             const lhs_sent = Air.internedToRef(lhs_sent_val.toIntern());
  14431             if (rhs_info.sentinel) |rhs_sent_val| {
  14432                 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern());
  14433                 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
  14434                 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
  14435                 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?;
  14436                 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?;
  14437                 if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) {
  14438                     break :s lhs_sent_casted_val;
  14439                 } else {
  14440                     break :s null;
  14441                 }
  14442             } else {
  14443                 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
  14444                 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?;
  14445                 break :s lhs_sent_casted_val;
  14446             }
  14447         } else {
  14448             if (rhs_info.sentinel) |rhs_sent_val| {
  14449                 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern());
  14450                 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
  14451                 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?;
  14452                 break :s rhs_sent_casted_val;
  14453             } else {
  14454                 break :s null;
  14455             }
  14456         }
  14457     };
  14458 
  14459     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
  14460     const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len);
  14461     const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) {
  14462         error.Overflow => return sema.fail(
  14463             block,
  14464             src,
  14465             "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
  14466             .{ lhs_len, rhs_len },
  14467         ),
  14468     };
  14469 
  14470     const result_ty = try pt.arrayType(.{
  14471         .len = result_len,
  14472         .sentinel = if (res_sent_val) |v| v.toIntern() else .none,
  14473         .child = resolved_elem_ty.toIntern(),
  14474     });
  14475     const ptr_addrspace = p: {
  14476         if (lhs_ty.zigTypeTag(zcu) == .pointer) break :p lhs_ty.ptrAddressSpace(zcu);
  14477         if (rhs_ty.zigTypeTag(zcu) == .pointer) break :p rhs_ty.ptrAddressSpace(zcu);
  14478         break :p null;
  14479     };
  14480 
  14481     const runtime_src = if (switch (lhs_ty.zigTypeTag(zcu)) {
  14482         .array, .@"struct" => try sema.resolveValue(lhs),
  14483         .pointer => try sema.resolveDefinedValue(block, lhs_src, lhs),
  14484         else => unreachable,
  14485     }) |lhs_val| rs: {
  14486         if (switch (rhs_ty.zigTypeTag(zcu)) {
  14487             .array, .@"struct" => try sema.resolveValue(rhs),
  14488             .pointer => try sema.resolveDefinedValue(block, rhs_src, rhs),
  14489             else => unreachable,
  14490         }) |rhs_val| {
  14491             const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu))
  14492                 try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :rs lhs_src
  14493             else if (lhs_ty.isSlice(zcu))
  14494                 try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :rs lhs_src
  14495             else
  14496                 lhs_val;
  14497 
  14498             const rhs_sub_val = if (rhs_ty.isSinglePointer(zcu))
  14499                 try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty) orelse break :rs rhs_src
  14500             else if (rhs_ty.isSlice(zcu))
  14501                 try sema.maybeDerefSliceAsArray(block, rhs_src, rhs_val) orelse break :rs rhs_src
  14502             else
  14503                 rhs_val;
  14504 
  14505             const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
  14506             var elem_i: u32 = 0;
  14507             while (elem_i < lhs_len) : (elem_i += 1) {
  14508                 const lhs_elem_i = elem_i;
  14509                 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else Value.@"unreachable";
  14510                 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(pt, lhs_elem_i) else elem_default_val;
  14511                 const elem_val_inst = Air.internedToRef(elem_val.toIntern());
  14512                 const operand_src = block.src(.{ .array_cat_lhs = .{
  14513                     .array_cat_offset = inst_data.src_node,
  14514                     .elem_index = elem_i,
  14515                 } });
  14516                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src);
  14517                 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined);
  14518                 element_vals[elem_i] = coerced_elem_val.toIntern();
  14519             }
  14520             while (elem_i < result_len) : (elem_i += 1) {
  14521                 const rhs_elem_i = elem_i - lhs_len;
  14522                 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else Value.@"unreachable";
  14523                 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(pt, rhs_elem_i) else elem_default_val;
  14524                 const elem_val_inst = Air.internedToRef(elem_val.toIntern());
  14525                 const operand_src = block.src(.{ .array_cat_rhs = .{
  14526                     .array_cat_offset = inst_data.src_node,
  14527                     .elem_index = @intCast(rhs_elem_i),
  14528                 } });
  14529                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src);
  14530                 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined);
  14531                 element_vals[elem_i] = coerced_elem_val.toIntern();
  14532             }
  14533             return sema.addConstantMaybeRef(
  14534                 (try pt.aggregateValue(result_ty, element_vals)).toIntern(),
  14535                 ptr_addrspace != null,
  14536             );
  14537         } else break :rs rhs_src;
  14538     } else lhs_src;
  14539 
  14540     try sema.requireRuntimeBlock(block, src, runtime_src);
  14541 
  14542     if (ptr_addrspace) |ptr_as| {
  14543         const constant_alloc_ty = try pt.ptrTypeSema(.{
  14544             .child = result_ty.toIntern(),
  14545             .flags = .{
  14546                 .address_space = ptr_as,
  14547                 .is_const = true,
  14548             },
  14549         });
  14550         const alloc_ty = try pt.ptrTypeSema(.{
  14551             .child = result_ty.toIntern(),
  14552             .flags = .{ .address_space = ptr_as },
  14553         });
  14554         const elem_ptr_ty = try pt.ptrTypeSema(.{
  14555             .child = resolved_elem_ty.toIntern(),
  14556             .flags = .{ .address_space = ptr_as },
  14557         });
  14558 
  14559         const mutable_alloc = try block.addTy(.alloc, alloc_ty);
  14560 
  14561         // if both the source and destination are arrays
  14562         // we can hotpath via a memcpy.
  14563         if (lhs_ty.zigTypeTag(zcu) == .pointer and
  14564             rhs_ty.zigTypeTag(zcu) == .pointer)
  14565         {
  14566             const slice_ty = try pt.ptrTypeSema(.{
  14567                 .child = resolved_elem_ty.toIntern(),
  14568                 .flags = .{
  14569                     .size = .slice,
  14570                     .address_space = ptr_as,
  14571                 },
  14572             });
  14573 
  14574             const many_ty = slice_ty.slicePtrFieldType(zcu);
  14575             const many_alloc = try block.addBitCast(many_ty, mutable_alloc);
  14576 
  14577             // lhs_dest_slice = dest[0..lhs.len]
  14578             const slice_ty_ref = Air.internedToRef(slice_ty.toIntern());
  14579             const lhs_len_ref = try pt.intRef(.usize, lhs_len);
  14580             const lhs_dest_slice = try block.addInst(.{
  14581                 .tag = .slice,
  14582                 .data = .{ .ty_pl = .{
  14583                     .ty = slice_ty_ref,
  14584                     .payload = try sema.addExtra(Air.Bin{
  14585                         .lhs = many_alloc,
  14586                         .rhs = lhs_len_ref,
  14587                     }),
  14588                 } },
  14589             });
  14590 
  14591             _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs);
  14592 
  14593             // rhs_dest_slice = dest[lhs.len..][0..rhs.len]
  14594             const rhs_len_ref = try pt.intRef(.usize, rhs_len);
  14595             const rhs_dest_offset = try block.addInst(.{
  14596                 .tag = .ptr_add,
  14597                 .data = .{ .ty_pl = .{
  14598                     .ty = Air.internedToRef(many_ty.toIntern()),
  14599                     .payload = try sema.addExtra(Air.Bin{
  14600                         .lhs = many_alloc,
  14601                         .rhs = lhs_len_ref,
  14602                     }),
  14603                 } },
  14604             });
  14605             const rhs_dest_slice = try block.addInst(.{
  14606                 .tag = .slice,
  14607                 .data = .{ .ty_pl = .{
  14608                     .ty = slice_ty_ref,
  14609                     .payload = try sema.addExtra(Air.Bin{
  14610                         .lhs = rhs_dest_offset,
  14611                         .rhs = rhs_len_ref,
  14612                     }),
  14613                 } },
  14614             });
  14615 
  14616             _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs);
  14617 
  14618             if (res_sent_val) |sent_val| {
  14619                 const elem_index = try pt.intRef(.usize, result_len);
  14620                 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
  14621                 const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern());
  14622                 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  14623             }
  14624 
  14625             return block.addBitCast(constant_alloc_ty, mutable_alloc);
  14626         }
  14627 
  14628         var elem_i: u32 = 0;
  14629         while (elem_i < lhs_len) : (elem_i += 1) {
  14630             const elem_index = try pt.intRef(.usize, elem_i);
  14631             const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
  14632             const operand_src = block.src(.{ .array_cat_lhs = .{
  14633                 .array_cat_offset = inst_data.src_node,
  14634                 .elem_index = elem_i,
  14635             } });
  14636             const init = try sema.elemVal(block, operand_src, lhs, elem_index, src, true);
  14637             try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store);
  14638         }
  14639         while (elem_i < result_len) : (elem_i += 1) {
  14640             const rhs_elem_i = elem_i - lhs_len;
  14641             const elem_index = try pt.intRef(.usize, elem_i);
  14642             const rhs_index = try pt.intRef(.usize, rhs_elem_i);
  14643             const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
  14644             const operand_src = block.src(.{ .array_cat_rhs = .{
  14645                 .array_cat_offset = inst_data.src_node,
  14646                 .elem_index = @intCast(rhs_elem_i),
  14647             } });
  14648             const init = try sema.elemVal(block, operand_src, rhs, rhs_index, src, true);
  14649             try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store);
  14650         }
  14651         if (res_sent_val) |sent_val| {
  14652             const elem_index = try pt.intRef(.usize, result_len);
  14653             const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
  14654             const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern());
  14655             try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  14656         }
  14657 
  14658         return block.addBitCast(constant_alloc_ty, mutable_alloc);
  14659     }
  14660 
  14661     const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
  14662     {
  14663         var elem_i: u32 = 0;
  14664         while (elem_i < lhs_len) : (elem_i += 1) {
  14665             const index = try pt.intRef(.usize, elem_i);
  14666             const operand_src = block.src(.{ .array_cat_lhs = .{
  14667                 .array_cat_offset = inst_data.src_node,
  14668                 .elem_index = elem_i,
  14669             } });
  14670             const init = try sema.elemVal(block, operand_src, lhs, index, src, true);
  14671             element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src);
  14672         }
  14673         while (elem_i < result_len) : (elem_i += 1) {
  14674             const rhs_elem_i = elem_i - lhs_len;
  14675             const index = try pt.intRef(.usize, rhs_elem_i);
  14676             const operand_src = block.src(.{ .array_cat_rhs = .{
  14677                 .array_cat_offset = inst_data.src_node,
  14678                 .elem_index = @intCast(rhs_elem_i),
  14679             } });
  14680             const init = try sema.elemVal(block, operand_src, rhs, index, src, true);
  14681             element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src);
  14682         }
  14683     }
  14684 
  14685     return block.addAggregateInit(result_ty, element_refs);
  14686 }
  14687 
  14688 fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo {
  14689     const pt = sema.pt;
  14690     const zcu = pt.zcu;
  14691     const operand_ty = sema.typeOf(operand);
  14692     switch (operand_ty.zigTypeTag(zcu)) {
  14693         .array => return operand_ty.arrayInfo(zcu),
  14694         .pointer => {
  14695             const ptr_info = operand_ty.ptrInfo(zcu);
  14696             switch (ptr_info.flags.size) {
  14697                 .slice => {
  14698                     const val = try sema.resolveConstDefinedValue(block, src, operand, .{ .simple = .slice_cat_operand });
  14699                     return .{
  14700                         .elem_type = .fromInterned(ptr_info.child),
  14701                         .sentinel = switch (ptr_info.sentinel) {
  14702                             .none => null,
  14703                             else => Value.fromInterned(ptr_info.sentinel),
  14704                         },
  14705                         .len = try val.sliceLen(pt),
  14706                     };
  14707                 },
  14708                 .one => {
  14709                     if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) {
  14710                         return Type.fromInterned(ptr_info.child).arrayInfo(zcu);
  14711                     }
  14712                 },
  14713                 .c, .many => {},
  14714             }
  14715         },
  14716         .@"struct" => {
  14717             if (operand_ty.isTuple(zcu) and peer_ty.isIndexable(zcu)) {
  14718                 assert(!peer_ty.isTuple(zcu));
  14719                 return .{
  14720                     .elem_type = peer_ty.elemType2(zcu),
  14721                     .sentinel = null,
  14722                     .len = operand_ty.arrayLen(zcu),
  14723                 };
  14724             }
  14725         },
  14726         else => {},
  14727     }
  14728     return null;
  14729 }
  14730 
  14731 fn analyzeTupleMul(
  14732     sema: *Sema,
  14733     block: *Block,
  14734     src_node: std.zig.Ast.Node.Offset,
  14735     operand: Air.Inst.Ref,
  14736     factor: usize,
  14737 ) CompileError!Air.Inst.Ref {
  14738     const pt = sema.pt;
  14739     const zcu = pt.zcu;
  14740     const operand_ty = sema.typeOf(operand);
  14741     const src = block.nodeOffset(src_node);
  14742     const len_src = block.src(.{ .node_offset_bin_rhs = src_node });
  14743 
  14744     const tuple_len = operand_ty.structFieldCount(zcu);
  14745     const final_len = std.math.mul(usize, tuple_len, factor) catch
  14746         return sema.fail(block, len_src, "operation results in overflow", .{});
  14747 
  14748     if (final_len == 0) {
  14749         return .empty_tuple;
  14750     }
  14751     const types = try sema.arena.alloc(InternPool.Index, final_len);
  14752     const values = try sema.arena.alloc(InternPool.Index, final_len);
  14753 
  14754     const opt_runtime_src = rs: {
  14755         var runtime_src: ?LazySrcLoc = null;
  14756         for (0..tuple_len) |i| {
  14757             types[i] = operand_ty.fieldType(i, zcu).toIntern();
  14758             values[i] = operand_ty.structFieldDefaultValue(i, zcu).toIntern();
  14759             const operand_src = block.src(.{ .array_cat_lhs = .{
  14760                 .array_cat_offset = src_node,
  14761                 .elem_index = @intCast(i),
  14762             } });
  14763             if (values[i] == .unreachable_value) {
  14764                 runtime_src = operand_src;
  14765                 values[i] = .none; // TODO don't treat unreachable_value as special
  14766             }
  14767         }
  14768         for (0..factor) |i| {
  14769             @memmove(types[tuple_len * i ..][0..tuple_len], types[0..tuple_len]);
  14770             @memmove(values[tuple_len * i ..][0..tuple_len], values[0..tuple_len]);
  14771         }
  14772         break :rs runtime_src;
  14773     };
  14774 
  14775     const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{
  14776         .types = types,
  14777         .values = values,
  14778     }));
  14779 
  14780     const runtime_src = opt_runtime_src orelse {
  14781         const tuple_val = try pt.aggregateValue(tuple_ty, values);
  14782         return Air.internedToRef(tuple_val.toIntern());
  14783     };
  14784 
  14785     try sema.requireRuntimeBlock(block, src, runtime_src);
  14786 
  14787     const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
  14788     var i: u32 = 0;
  14789     while (i < tuple_len) : (i += 1) {
  14790         element_refs[i] = try sema.tupleFieldValByIndex(block, operand, @intCast(i), operand_ty);
  14791     }
  14792     i = 1;
  14793     while (i < factor) : (i += 1) {
  14794         @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]);
  14795     }
  14796 
  14797     return block.addAggregateInit(tuple_ty, element_refs);
  14798 }
  14799 
  14800 fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14801     const tracy = trace(@src());
  14802     defer tracy.end();
  14803 
  14804     const pt = sema.pt;
  14805     const zcu = pt.zcu;
  14806     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14807     const extra = sema.code.extraData(Zir.Inst.ArrayMul, inst_data.payload_index).data;
  14808     const uncoerced_lhs = try sema.resolveInst(extra.lhs);
  14809     const uncoerced_lhs_ty = sema.typeOf(uncoerced_lhs);
  14810     const src: LazySrcLoc = block.nodeOffset(inst_data.src_node);
  14811     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  14812     const operator_src = block.src(.{ .node_offset_main_token = inst_data.src_node });
  14813     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  14814 
  14815     const lhs, const lhs_ty = coerced_lhs: {
  14816         // If we have a result type, we might be able to do this more efficiently
  14817         // by coercing the LHS first. Specifically, if we want an array or vector
  14818         // and have a tuple, coerce the tuple immediately.
  14819         no_coerce: {
  14820             if (extra.res_ty == .none) break :no_coerce;
  14821             const res_ty = try sema.resolveTypeOrPoison(block, src, extra.res_ty) orelse break :no_coerce;
  14822             if (!uncoerced_lhs_ty.isTuple(zcu)) break :no_coerce;
  14823             const lhs_len = uncoerced_lhs_ty.structFieldCount(zcu);
  14824             const lhs_dest_ty = switch (res_ty.zigTypeTag(zcu)) {
  14825                 else => break :no_coerce,
  14826                 .array => try pt.arrayType(.{
  14827                     .child = res_ty.childType(zcu).toIntern(),
  14828                     .len = lhs_len,
  14829                     .sentinel = if (res_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  14830                 }),
  14831                 .vector => try pt.vectorType(.{
  14832                     .child = res_ty.childType(zcu).toIntern(),
  14833                     .len = lhs_len,
  14834                 }),
  14835             };
  14836             // Attempt to coerce to this type, but don't emit an error if it fails. Instead,
  14837             // just exit out of this path and let the usual error happen later, so that error
  14838             // messages are consistent.
  14839             const coerced = sema.coerceExtra(block, lhs_dest_ty, uncoerced_lhs, lhs_src, .{ .report_err = false }) catch |err| switch (err) {
  14840                 error.NotCoercible => break :no_coerce,
  14841                 else => |e| return e,
  14842             };
  14843             break :coerced_lhs .{ coerced, lhs_dest_ty };
  14844         }
  14845         break :coerced_lhs .{ uncoerced_lhs, uncoerced_lhs_ty };
  14846     };
  14847 
  14848     if (lhs_ty.isTuple(zcu)) {
  14849         // In `**` rhs must be comptime-known, but lhs can be runtime-known
  14850         const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor });
  14851         const factor_casted = try sema.usizeCast(block, rhs_src, factor);
  14852         return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted);
  14853     }
  14854 
  14855     // Analyze the lhs first, to catch the case that someone tried to do exponentiation
  14856     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse {
  14857         const msg = msg: {
  14858             const msg = try sema.errMsg(lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)});
  14859             errdefer msg.destroy(sema.gpa);
  14860             switch (lhs_ty.zigTypeTag(zcu)) {
  14861                 .int, .float, .comptime_float, .comptime_int, .vector => {
  14862                     try sema.errNote(operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{});
  14863                 },
  14864                 else => {},
  14865             }
  14866             break :msg msg;
  14867         };
  14868         return sema.failWithOwnedErrorMsg(block, msg);
  14869     };
  14870 
  14871     // In `**` rhs must be comptime-known, but lhs can be runtime-known
  14872     const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor });
  14873 
  14874     const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch
  14875         return sema.fail(block, rhs_src, "operation results in overflow", .{});
  14876     const result_len = try sema.usizeCast(block, src, result_len_u64);
  14877 
  14878     const result_ty = try pt.arrayType(.{
  14879         .len = result_len,
  14880         .sentinel = if (lhs_info.sentinel) |s| s.toIntern() else .none,
  14881         .child = lhs_info.elem_type.toIntern(),
  14882     });
  14883 
  14884     const ptr_addrspace = if (lhs_ty.zigTypeTag(zcu) == .pointer) lhs_ty.ptrAddressSpace(zcu) else null;
  14885     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
  14886 
  14887     if (try sema.resolveValue(lhs)) |lhs_val| ct: {
  14888         const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu))
  14889             try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :ct
  14890         else if (lhs_ty.isSlice(zcu))
  14891             try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :ct
  14892         else
  14893             lhs_val;
  14894 
  14895         const val = v: {
  14896             // Optimization for the common pattern of a single element repeated N times, such
  14897             // as zero-filling a byte array.
  14898             if (lhs_len == 1 and lhs_info.sentinel == null) {
  14899                 const elem_val = try lhs_sub_val.elemValue(pt, 0);
  14900                 break :v try pt.aggregateSplatValue(result_ty, elem_val);
  14901             }
  14902 
  14903             const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
  14904             var elem_i: usize = 0;
  14905             while (elem_i < result_len) {
  14906                 var lhs_i: usize = 0;
  14907                 while (lhs_i < lhs_len) : (lhs_i += 1) {
  14908                     const elem_val = try lhs_sub_val.elemValue(pt, lhs_i);
  14909                     element_vals[elem_i] = elem_val.toIntern();
  14910                     elem_i += 1;
  14911                 }
  14912             }
  14913             break :v try pt.aggregateValue(result_ty, element_vals);
  14914         };
  14915         return sema.addConstantMaybeRef(val.toIntern(), ptr_addrspace != null);
  14916     }
  14917 
  14918     try sema.requireRuntimeBlock(block, src, lhs_src);
  14919 
  14920     // Grab all the LHS values ahead of time, rather than repeatedly emitting instructions
  14921     // to get the same elem values.
  14922     const lhs_vals = try sema.arena.alloc(Air.Inst.Ref, lhs_len);
  14923     for (lhs_vals, 0..) |*lhs_val, idx| {
  14924         const idx_ref = try pt.intRef(.usize, idx);
  14925         lhs_val.* = try sema.elemVal(block, lhs_src, lhs, idx_ref, src, false);
  14926     }
  14927 
  14928     if (ptr_addrspace) |ptr_as| {
  14929         const alloc_ty = try pt.ptrTypeSema(.{
  14930             .child = result_ty.toIntern(),
  14931             .flags = .{
  14932                 .address_space = ptr_as,
  14933                 .is_const = true,
  14934             },
  14935         });
  14936         const alloc = try block.addTy(.alloc, alloc_ty);
  14937         const elem_ptr_ty = try pt.ptrTypeSema(.{
  14938             .child = lhs_info.elem_type.toIntern(),
  14939             .flags = .{ .address_space = ptr_as },
  14940         });
  14941 
  14942         var elem_i: usize = 0;
  14943         while (elem_i < result_len) {
  14944             for (lhs_vals) |lhs_val| {
  14945                 const elem_index = try pt.intRef(.usize, elem_i);
  14946                 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  14947                 try sema.storePtr2(block, src, elem_ptr, src, lhs_val, lhs_src, .store);
  14948                 elem_i += 1;
  14949             }
  14950         }
  14951         if (lhs_info.sentinel) |sent_val| {
  14952             const elem_index = try pt.intRef(.usize, result_len);
  14953             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  14954             const init = Air.internedToRef(sent_val.toIntern());
  14955             try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  14956         }
  14957 
  14958         return alloc;
  14959     }
  14960 
  14961     const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
  14962     for (0..try sema.usizeCast(block, rhs_src, factor)) |i| {
  14963         @memcpy(element_refs[i * lhs_len ..][0..lhs_len], lhs_vals);
  14964     }
  14965     return block.addAggregateInit(result_ty, element_refs);
  14966 }
  14967 
  14968 fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14969     const pt = sema.pt;
  14970     const zcu = pt.zcu;
  14971     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  14972     const src = block.nodeOffset(inst_data.src_node);
  14973     const lhs_src = src;
  14974     const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
  14975 
  14976     const rhs = try sema.resolveInst(inst_data.operand);
  14977     const rhs_ty = sema.typeOf(rhs);
  14978     const rhs_scalar_ty = rhs_ty.scalarType(zcu);
  14979 
  14980     if (rhs_scalar_ty.isUnsignedInt(zcu) or switch (rhs_scalar_ty.zigTypeTag(zcu)) {
  14981         .int, .comptime_int, .float, .comptime_float => false,
  14982         else => true,
  14983     }) {
  14984         return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)});
  14985     }
  14986 
  14987     if (rhs_scalar_ty.isAnyFloat()) {
  14988         // We handle float negation here to ensure negative zero is represented in the bits.
  14989         if (try sema.resolveValue(rhs)) |rhs_val| {
  14990             const result = try arith.negateFloat(sema, rhs_ty, rhs_val);
  14991             return Air.internedToRef(result.toIntern());
  14992         }
  14993         try sema.requireRuntimeBlock(block, src, null);
  14994         return block.addUnOp(if (block.float_mode == .optimized) .neg_optimized else .neg, rhs);
  14995     }
  14996 
  14997     const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern());
  14998     return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true);
  14999 }
  15000 
  15001 fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15002     const pt = sema.pt;
  15003     const zcu = pt.zcu;
  15004     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  15005     const src = block.nodeOffset(inst_data.src_node);
  15006     const lhs_src = src;
  15007     const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
  15008 
  15009     const rhs = try sema.resolveInst(inst_data.operand);
  15010     const rhs_ty = sema.typeOf(rhs);
  15011     const rhs_scalar_ty = rhs_ty.scalarType(zcu);
  15012 
  15013     switch (rhs_scalar_ty.zigTypeTag(zcu)) {
  15014         .int, .comptime_int, .float, .comptime_float => {},
  15015         else => return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)}),
  15016     }
  15017 
  15018     const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern());
  15019     return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true);
  15020 }
  15021 
  15022 fn zirArithmetic(
  15023     sema: *Sema,
  15024     block: *Block,
  15025     inst: Zir.Inst.Index,
  15026     zir_tag: Zir.Inst.Tag,
  15027     safety: bool,
  15028 ) CompileError!Air.Inst.Ref {
  15029     const tracy = trace(@src());
  15030     defer tracy.end();
  15031 
  15032     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15033     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15034     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  15035     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  15036     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15037     const lhs = try sema.resolveInst(extra.lhs);
  15038     const rhs = try sema.resolveInst(extra.rhs);
  15039 
  15040     return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, src, lhs_src, rhs_src, safety);
  15041 }
  15042 
  15043 fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15044     const pt = sema.pt;
  15045     const zcu = pt.zcu;
  15046     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15047     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15048     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  15049     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  15050     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15051     const lhs = try sema.resolveInst(extra.lhs);
  15052     const rhs = try sema.resolveInst(extra.rhs);
  15053     const lhs_ty = sema.typeOf(lhs);
  15054     const rhs_ty = sema.typeOf(rhs);
  15055     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15056     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15057     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15058     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15059 
  15060     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15061         .override = &.{ lhs_src, rhs_src },
  15062     });
  15063 
  15064     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15065     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15066 
  15067     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  15068     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15069 
  15070     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15071 
  15072     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div);
  15073 
  15074     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15075     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15076 
  15077     if ((lhs_ty.zigTypeTag(zcu) == .comptime_float and rhs_ty.zigTypeTag(zcu) == .comptime_int) or
  15078         (lhs_ty.zigTypeTag(zcu) == .comptime_int and rhs_ty.zigTypeTag(zcu) == .comptime_float))
  15079     {
  15080         // If it makes a difference whether we coerce to ints or floats before doing the division, error.
  15081         // If lhs % rhs is 0, it doesn't matter.
  15082         const lhs_val = maybe_lhs_val orelse unreachable;
  15083         const rhs_val = maybe_rhs_val orelse unreachable;
  15084         const rem = arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem) catch unreachable;
  15085         if (!rem.compareAllWithZero(.eq, zcu)) {
  15086             return sema.fail(
  15087                 block,
  15088                 src,
  15089                 "ambiguous coercion of division operands '{f}' and '{f}'; non-zero remainder '{f}'",
  15090                 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt), rem.fmtValueSema(pt, sema) },
  15091             );
  15092         }
  15093     }
  15094 
  15095     // TODO: emit compile error when .div is used on integers and there would be an
  15096     // ambiguous result between div_floor and div_trunc.
  15097 
  15098     // The rules here are like those in `analyzeArithmetic`:
  15099     //
  15100     // * If both operands are comptime-known, call the corresponding function in `arith`.
  15101     //   Inputs which would be IB at runtime are compile errors.
  15102     //
  15103     // * Otherwise, if one operand is comptime-known `undefined`, we either trigger a compile error
  15104     //   or return `undefined`, depending on whether this operator can trigger IB.
  15105     //
  15106     // * No other comptime operand determines a comptime result, so remaining cases are runtime ops.
  15107 
  15108     const allow_div_zero = !is_int and
  15109         resolved_type.toIntern() != .comptime_float_type and
  15110         block.float_mode == .strict;
  15111 
  15112     if (maybe_lhs_val) |lhs_val| {
  15113         if (maybe_rhs_val) |rhs_val| {
  15114             return .fromValue(try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div));
  15115         }
  15116         if (allow_div_zero) {
  15117             if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15118         } else {
  15119             try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  15120         }
  15121     } else if (maybe_rhs_val) |rhs_val| {
  15122         if (allow_div_zero) {
  15123             if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15124         } else {
  15125             try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
  15126             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15127         }
  15128     }
  15129 
  15130     if (block.wantSafety()) {
  15131         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  15132         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15133     }
  15134 
  15135     const air_tag: Air.Inst.Tag = if (is_int) blk: {
  15136         if (lhs_ty.isSignedInt(zcu) or rhs_ty.isSignedInt(zcu)) {
  15137             return sema.fail(
  15138                 block,
  15139                 src,
  15140                 "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, or @divExact",
  15141                 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) },
  15142             );
  15143         }
  15144         break :blk .div_trunc;
  15145     } else switch (block.float_mode) {
  15146         .optimized => .div_float_optimized,
  15147         .strict => .div_float,
  15148     };
  15149     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15150 }
  15151 
  15152 fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15153     const pt = sema.pt;
  15154     const zcu = pt.zcu;
  15155     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15156     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15157     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  15158     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  15159     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15160     const lhs = try sema.resolveInst(extra.lhs);
  15161     const rhs = try sema.resolveInst(extra.rhs);
  15162     const lhs_ty = sema.typeOf(lhs);
  15163     const rhs_ty = sema.typeOf(rhs);
  15164     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15165     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15166     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15167     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15168 
  15169     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15170         .override = &.{ lhs_src, rhs_src },
  15171     });
  15172 
  15173     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15174     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15175 
  15176     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  15177     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15178 
  15179     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15180 
  15181     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact);
  15182 
  15183     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15184     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15185 
  15186     // Because `@divExact` can trigger Illegal Behavior, undefined operands trigger Illegal Behavior.
  15187 
  15188     if (maybe_lhs_val) |lhs_val| {
  15189         if (maybe_rhs_val) |rhs_val| {
  15190             const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_exact);
  15191             return Air.internedToRef(result.toIntern());
  15192         }
  15193         try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  15194     } else if (maybe_rhs_val) |rhs_val| {
  15195         try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
  15196         if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15197     }
  15198 
  15199     // Depending on whether safety is enabled, we will have a slightly different strategy
  15200     // here. The `div_exact` AIR instruction causes illegal behavior if a remainder
  15201     // is produced, so in the safety check case, it cannot be used. Instead we do a
  15202     // div_trunc and check for remainder.
  15203 
  15204     if (block.wantSafety()) {
  15205         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  15206         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15207 
  15208         const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs);
  15209         const ok = if (!is_int) ok: {
  15210             const floored = try block.addUnOp(.floor, result);
  15211 
  15212             if (resolved_type.zigTypeTag(zcu) == .vector) {
  15213                 const eql = try block.addCmpVector(result, floored, .eq);
  15214                 break :ok try block.addReduce(eql, .And);
  15215             } else {
  15216                 const is_in_range = try block.addBinOp(switch (block.float_mode) {
  15217                     .strict => .cmp_eq,
  15218                     .optimized => .cmp_eq_optimized,
  15219                 }, result, floored);
  15220                 break :ok is_in_range;
  15221             }
  15222         } else ok: {
  15223             const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
  15224 
  15225             const scalar_zero = switch (scalar_tag) {
  15226                 .comptime_float, .float => try pt.floatValue(resolved_type.scalarType(zcu), 0.0),
  15227                 .comptime_int, .int => try pt.intValue(resolved_type.scalarType(zcu), 0),
  15228                 else => unreachable,
  15229             };
  15230             if (resolved_type.zigTypeTag(zcu) == .vector) {
  15231                 const zero_val = try sema.splat(resolved_type, scalar_zero);
  15232                 const zero = Air.internedToRef(zero_val.toIntern());
  15233                 const eql = try block.addCmpVector(remainder, zero, .eq);
  15234                 break :ok try block.addReduce(eql, .And);
  15235             } else {
  15236                 const zero = Air.internedToRef(scalar_zero.toIntern());
  15237                 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero);
  15238                 break :ok is_in_range;
  15239             }
  15240         };
  15241         try sema.addSafetyCheck(block, src, ok, .exact_division_remainder);
  15242         return result;
  15243     }
  15244 
  15245     return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs);
  15246 }
  15247 
  15248 fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15249     const pt = sema.pt;
  15250     const zcu = pt.zcu;
  15251     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15252     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15253     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  15254     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  15255     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15256     const lhs = try sema.resolveInst(extra.lhs);
  15257     const rhs = try sema.resolveInst(extra.rhs);
  15258     const lhs_ty = sema.typeOf(lhs);
  15259     const rhs_ty = sema.typeOf(rhs);
  15260     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15261     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15262     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15263     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15264 
  15265     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15266         .override = &.{ lhs_src, rhs_src },
  15267     });
  15268 
  15269     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15270     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15271 
  15272     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  15273     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15274 
  15275     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15276 
  15277     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor);
  15278 
  15279     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15280     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15281 
  15282     const allow_div_zero = !is_int and
  15283         resolved_type.toIntern() != .comptime_float_type and
  15284         block.float_mode == .strict;
  15285 
  15286     if (maybe_lhs_val) |lhs_val| {
  15287         if (maybe_rhs_val) |rhs_val| {
  15288             const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_floor);
  15289             return Air.internedToRef(result.toIntern());
  15290         }
  15291         if (allow_div_zero) {
  15292             if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15293         } else {
  15294             try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  15295         }
  15296     } else if (maybe_rhs_val) |rhs_val| {
  15297         if (allow_div_zero) {
  15298             if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15299         } else {
  15300             try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
  15301             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15302         }
  15303     }
  15304 
  15305     if (block.wantSafety()) {
  15306         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  15307         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15308     }
  15309 
  15310     return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
  15311 }
  15312 
  15313 fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15314     const pt = sema.pt;
  15315     const zcu = pt.zcu;
  15316     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15317     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15318     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  15319     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  15320     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15321     const lhs = try sema.resolveInst(extra.lhs);
  15322     const rhs = try sema.resolveInst(extra.rhs);
  15323     const lhs_ty = sema.typeOf(lhs);
  15324     const rhs_ty = sema.typeOf(rhs);
  15325     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15326     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15327     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15328     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15329 
  15330     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15331         .override = &.{ lhs_src, rhs_src },
  15332     });
  15333 
  15334     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15335     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15336 
  15337     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  15338     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15339 
  15340     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15341 
  15342     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc);
  15343 
  15344     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15345     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15346 
  15347     const allow_div_zero = !is_int and
  15348         resolved_type.toIntern() != .comptime_float_type and
  15349         block.float_mode == .strict;
  15350 
  15351     if (maybe_lhs_val) |lhs_val| {
  15352         if (maybe_rhs_val) |rhs_val| {
  15353             const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_trunc);
  15354             return Air.internedToRef(result.toIntern());
  15355         }
  15356         if (allow_div_zero) {
  15357             if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15358         } else {
  15359             try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  15360         }
  15361     } else if (maybe_rhs_val) |rhs_val| {
  15362         if (allow_div_zero) {
  15363             if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15364         } else {
  15365             try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
  15366             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15367         }
  15368     }
  15369 
  15370     if (block.wantSafety()) {
  15371         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  15372         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15373     }
  15374 
  15375     return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs);
  15376 }
  15377 
  15378 fn addDivIntOverflowSafety(
  15379     sema: *Sema,
  15380     block: *Block,
  15381     src: LazySrcLoc,
  15382     resolved_type: Type,
  15383     lhs_scalar_ty: Type,
  15384     maybe_lhs_val: ?Value,
  15385     maybe_rhs_val: ?Value,
  15386     casted_lhs: Air.Inst.Ref,
  15387     casted_rhs: Air.Inst.Ref,
  15388     is_int: bool,
  15389 ) CompileError!void {
  15390     const pt = sema.pt;
  15391     const zcu = pt.zcu;
  15392     if (!is_int) return;
  15393 
  15394     // If the LHS is unsigned, it cannot cause overflow.
  15395     if (!lhs_scalar_ty.isSignedInt(zcu)) return;
  15396 
  15397     // If the LHS is widened to a larger integer type, no overflow is possible.
  15398     if (lhs_scalar_ty.intInfo(zcu).bits < resolved_type.intInfo(zcu).bits) {
  15399         return;
  15400     }
  15401 
  15402     const min_int = try resolved_type.minInt(pt, resolved_type);
  15403     const neg_one_scalar = try pt.intValue(lhs_scalar_ty, -1);
  15404     const neg_one = try sema.splat(resolved_type, neg_one_scalar);
  15405 
  15406     // If the LHS is comptime-known to be not equal to the min int,
  15407     // no overflow is possible.
  15408     if (maybe_lhs_val) |lhs_val| {
  15409         if (try lhs_val.compareAll(.neq, min_int, resolved_type, pt)) return;
  15410     }
  15411 
  15412     // If the RHS is comptime-known to not be equal to -1, no overflow is possible.
  15413     if (maybe_rhs_val) |rhs_val| {
  15414         if (try rhs_val.compareAll(.neq, neg_one, resolved_type, pt)) return;
  15415     }
  15416 
  15417     if (resolved_type.zigTypeTag(zcu) == .vector) {
  15418         const vec_len = resolved_type.vectorLen(zcu);
  15419 
  15420         // This is a bool vector whose elements are true if the LHS element does NOT equal `min_int`.
  15421         const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val) |lhs_val| ok: {
  15422             // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements.
  15423             const min_int_scalar = try min_int.elemValue(pt, 0);
  15424             const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len);
  15425             for (elems_ok, 0..) |*elem_ok, elem_idx| {
  15426                 const elem_val = try lhs_val.elemValue(pt, elem_idx);
  15427                 elem_ok.* = if (elem_val.eqlScalarNum(min_int_scalar, zcu)) .bool_false else .bool_true;
  15428             }
  15429             break :ok .fromValue(try pt.aggregateValue(try pt.vectorType(.{
  15430                 .len = vec_len,
  15431                 .child = .bool_type,
  15432             }), elems_ok));
  15433         } else ok: {
  15434             // The operand isn't comptime-known; add a runtime comparison.
  15435             const min_int_ref = Air.internedToRef(min_int.toIntern());
  15436             break :ok try block.addCmpVector(casted_lhs, min_int_ref, .neq);
  15437         };
  15438 
  15439         // This is a bool vector whose elements are true if the RHS element does NOT equal -1.
  15440         const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val) |rhs_val| ok: {
  15441             // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements.
  15442             const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len);
  15443             for (elems_ok, 0..) |*elem_ok, elem_idx| {
  15444                 const elem_val = try rhs_val.elemValue(pt, elem_idx);
  15445                 elem_ok.* = if (elem_val.eqlScalarNum(neg_one_scalar, zcu)) .bool_false else .bool_true;
  15446             }
  15447             break :ok .fromValue(try pt.aggregateValue(try pt.vectorType(.{
  15448                 .len = vec_len,
  15449                 .child = .bool_type,
  15450             }), elems_ok));
  15451         } else ok: {
  15452             // The operand isn't comptime-known; add a runtime comparison.
  15453             const neg_one_ref = Air.internedToRef(neg_one.toIntern());
  15454             break :ok try block.addCmpVector(casted_rhs, neg_one_ref, .neq);
  15455         };
  15456 
  15457         const ok = try block.addReduce(try block.addBinOp(.bool_or, lhs_ok, rhs_ok), .And);
  15458         try sema.addSafetyCheck(block, src, ok, .integer_overflow);
  15459     } else {
  15460         const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val == null) ok: {
  15461             const min_int_ref = Air.internedToRef(min_int.toIntern());
  15462             break :ok try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref);
  15463         } else .none; // means false
  15464         const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val == null) ok: {
  15465             const neg_one_ref = Air.internedToRef(neg_one.toIntern());
  15466             break :ok try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref);
  15467         } else .none; // means false
  15468 
  15469         const ok = if (lhs_ok != .none and rhs_ok != .none)
  15470             try block.addBinOp(.bool_or, lhs_ok, rhs_ok)
  15471         else if (lhs_ok != .none)
  15472             lhs_ok
  15473         else if (rhs_ok != .none)
  15474             rhs_ok
  15475         else
  15476             unreachable;
  15477 
  15478         try sema.addSafetyCheck(block, src, ok, .integer_overflow);
  15479     }
  15480 }
  15481 
  15482 fn addDivByZeroSafety(
  15483     sema: *Sema,
  15484     block: *Block,
  15485     src: LazySrcLoc,
  15486     resolved_type: Type,
  15487     maybe_rhs_val: ?Value,
  15488     casted_rhs: Air.Inst.Ref,
  15489     is_int: bool,
  15490 ) CompileError!void {
  15491     // Strict IEEE floats have well-defined division by zero.
  15492     if (!is_int and block.float_mode == .strict) return;
  15493 
  15494     // If rhs was comptime-known to be zero a compile error would have been
  15495     // emitted above.
  15496     if (maybe_rhs_val != null) return;
  15497 
  15498     const pt = sema.pt;
  15499     const zcu = pt.zcu;
  15500     const scalar_zero = if (is_int)
  15501         try pt.intValue(resolved_type.scalarType(zcu), 0)
  15502     else
  15503         try pt.floatValue(resolved_type.scalarType(zcu), 0.0);
  15504     const ok = if (resolved_type.zigTypeTag(zcu) == .vector) ok: {
  15505         const zero_val = try sema.splat(resolved_type, scalar_zero);
  15506         const zero = Air.internedToRef(zero_val.toIntern());
  15507         const ok = try block.addCmpVector(casted_rhs, zero, .neq);
  15508         break :ok try block.addReduce(ok, .And);
  15509     } else ok: {
  15510         const zero = Air.internedToRef(scalar_zero.toIntern());
  15511         break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero);
  15512     };
  15513     try sema.addSafetyCheck(block, src, ok, .divide_by_zero);
  15514 }
  15515 
  15516 fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag {
  15517     if (is_int) return normal;
  15518     return switch (block.float_mode) {
  15519         .strict => normal,
  15520         .optimized => optimized,
  15521     };
  15522 }
  15523 
  15524 fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15525     const pt = sema.pt;
  15526     const zcu = pt.zcu;
  15527     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15528     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15529     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  15530     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  15531     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15532     const lhs = try sema.resolveInst(extra.lhs);
  15533     const rhs = try sema.resolveInst(extra.rhs);
  15534     const lhs_ty = sema.typeOf(lhs);
  15535     const rhs_ty = sema.typeOf(rhs);
  15536     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15537     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15538     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15539     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15540 
  15541     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15542         .override = &.{ lhs_src, rhs_src },
  15543     });
  15544 
  15545     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15546     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15547 
  15548     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  15549     const rhs_scalar_ty = rhs_ty.scalarType(zcu);
  15550     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15551 
  15552     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15553 
  15554     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem);
  15555 
  15556     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15557     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15558 
  15559     const lhs_maybe_negative = a: {
  15560         if (lhs_scalar_ty.isUnsignedInt(zcu)) break :a false;
  15561         const lhs_val = maybe_lhs_val orelse break :a true;
  15562         if (lhs_val.compareAllWithZero(.gte, zcu)) break :a false;
  15563         break :a true;
  15564     };
  15565     const rhs_maybe_negative = a: {
  15566         if (rhs_scalar_ty.isUnsignedInt(zcu)) break :a false;
  15567         const rhs_val = maybe_rhs_val orelse break :a true;
  15568         if (rhs_val.compareAllWithZero(.gte, zcu)) break :a false;
  15569         break :a true;
  15570     };
  15571 
  15572     if (maybe_lhs_val) |lhs_val| {
  15573         if (maybe_rhs_val) |rhs_val| {
  15574             const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem);
  15575             if (lhs_maybe_negative or rhs_maybe_negative) {
  15576                 if (!result.compareAllWithZero(.eq, zcu)) {
  15577                     // Non-zero result means ambiguity between mod and rem
  15578                     return sema.failWithModRemNegative(block, src: {
  15579                         if (lhs_maybe_negative) break :src lhs_src;
  15580                         if (rhs_maybe_negative) break :src rhs_src;
  15581                         unreachable;
  15582                     }, lhs_ty, rhs_ty);
  15583                 }
  15584             }
  15585             return Air.internedToRef(result.toIntern());
  15586         }
  15587     }
  15588 
  15589     // Result not comptime-known, so floats and signed integers are illegal due to mod/rem ambiguity
  15590     if (lhs_maybe_negative or rhs_maybe_negative) {
  15591         return sema.failWithModRemNegative(block, src: {
  15592             if (lhs_maybe_negative) break :src lhs_src;
  15593             if (rhs_maybe_negative) break :src rhs_src;
  15594             unreachable;
  15595         }, lhs_ty, rhs_ty);
  15596     }
  15597 
  15598     const allow_div_zero = !is_int and
  15599         resolved_type.toIntern() != .comptime_float_type and
  15600         block.float_mode == .strict;
  15601 
  15602     if (maybe_lhs_val) |lhs_val| {
  15603         if (allow_div_zero) {
  15604             if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15605         } else {
  15606             try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  15607         }
  15608     } else if (maybe_rhs_val) |rhs_val| {
  15609         if (allow_div_zero) {
  15610             if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15611         } else {
  15612             try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
  15613             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15614         }
  15615     }
  15616 
  15617     if (block.wantSafety()) {
  15618         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15619     }
  15620 
  15621     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
  15622     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15623 }
  15624 
  15625 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15626     const pt = sema.pt;
  15627     const zcu = pt.zcu;
  15628     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15629     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15630     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  15631     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  15632     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15633     const lhs = try sema.resolveInst(extra.lhs);
  15634     const rhs = try sema.resolveInst(extra.rhs);
  15635     const lhs_ty = sema.typeOf(lhs);
  15636     const rhs_ty = sema.typeOf(rhs);
  15637     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15638     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15639     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15640     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15641 
  15642     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15643         .override = &.{ lhs_src, rhs_src },
  15644     });
  15645 
  15646     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15647     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15648 
  15649     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15650 
  15651     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15652 
  15653     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod);
  15654 
  15655     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15656     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15657 
  15658     const allow_div_zero = !is_int and
  15659         resolved_type.toIntern() != .comptime_float_type and
  15660         block.float_mode == .strict;
  15661 
  15662     if (maybe_lhs_val) |lhs_val| {
  15663         if (maybe_rhs_val) |rhs_val| {
  15664             const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .mod);
  15665             return Air.internedToRef(result.toIntern());
  15666         }
  15667         if (allow_div_zero) {
  15668             if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15669         } else {
  15670             try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  15671         }
  15672     } else if (maybe_rhs_val) |rhs_val| {
  15673         if (allow_div_zero) {
  15674             if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15675         } else {
  15676             try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
  15677             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15678         }
  15679     }
  15680 
  15681     if (block.wantSafety()) {
  15682         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15683     }
  15684 
  15685     const air_tag = airTag(block, is_int, .mod, .mod_optimized);
  15686     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15687 }
  15688 
  15689 fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15690     const pt = sema.pt;
  15691     const zcu = pt.zcu;
  15692     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15693     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15694     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  15695     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  15696     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15697     const lhs = try sema.resolveInst(extra.lhs);
  15698     const rhs = try sema.resolveInst(extra.rhs);
  15699     const lhs_ty = sema.typeOf(lhs);
  15700     const rhs_ty = sema.typeOf(rhs);
  15701     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15702     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15703     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15704     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15705 
  15706     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15707         .override = &.{ lhs_src, rhs_src },
  15708     });
  15709 
  15710     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15711     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15712 
  15713     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15714 
  15715     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15716 
  15717     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem);
  15718 
  15719     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15720     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15721 
  15722     const allow_div_zero = !is_int and
  15723         resolved_type.toIntern() != .comptime_float_type and
  15724         block.float_mode == .strict;
  15725 
  15726     if (maybe_lhs_val) |lhs_val| {
  15727         if (maybe_rhs_val) |rhs_val| {
  15728             const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem);
  15729             return Air.internedToRef(result.toIntern());
  15730         }
  15731         if (allow_div_zero) {
  15732             if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15733         } else {
  15734             try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  15735         }
  15736     } else if (maybe_rhs_val) |rhs_val| {
  15737         if (allow_div_zero) {
  15738             if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  15739         } else {
  15740             try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
  15741             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15742         }
  15743     }
  15744 
  15745     if (block.wantSafety()) {
  15746         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15747     }
  15748 
  15749     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
  15750     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15751 }
  15752 
  15753 fn zirOverflowArithmetic(
  15754     sema: *Sema,
  15755     block: *Block,
  15756     extended: Zir.Inst.Extended.InstData,
  15757     zir_tag: Zir.Inst.Extended,
  15758 ) CompileError!Air.Inst.Ref {
  15759     const tracy = trace(@src());
  15760     defer tracy.end();
  15761 
  15762     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  15763     const src = block.nodeOffset(extra.node);
  15764 
  15765     const lhs_src = block.builtinCallArgSrc(extra.node, 0);
  15766     const rhs_src = block.builtinCallArgSrc(extra.node, 1);
  15767 
  15768     const uncasted_lhs = try sema.resolveInst(extra.lhs);
  15769     const uncasted_rhs = try sema.resolveInst(extra.rhs);
  15770 
  15771     const lhs_ty = sema.typeOf(uncasted_lhs);
  15772     const rhs_ty = sema.typeOf(uncasted_rhs);
  15773     const pt = sema.pt;
  15774     const zcu = pt.zcu;
  15775     const ip = &zcu.intern_pool;
  15776 
  15777     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15778 
  15779     const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs };
  15780     const dest_ty = if (zir_tag == .shl_with_overflow)
  15781         lhs_ty
  15782     else
  15783         try sema.resolvePeerTypes(block, src, instructions, .{
  15784             .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  15785         });
  15786 
  15787     const rhs_dest_ty = if (zir_tag == .shl_with_overflow)
  15788         try sema.log2IntType(block, lhs_ty, src)
  15789     else
  15790         dest_ty;
  15791 
  15792     const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src);
  15793     const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src);
  15794 
  15795     if (dest_ty.scalarType(zcu).zigTypeTag(zcu) != .int) {
  15796         return sema.fail(block, src, "expected vector of integers or integer tag type, found '{f}'", .{dest_ty.fmt(pt)});
  15797     }
  15798 
  15799     const maybe_lhs_val = try sema.resolveValue(lhs);
  15800     const maybe_rhs_val = try sema.resolveValue(rhs);
  15801 
  15802     const tuple_ty = try pt.overflowArithmeticTupleType(dest_ty);
  15803     const overflow_ty: Type = .fromInterned(ip.indexToKey(tuple_ty.toIntern()).tuple_type.types.get(ip)[1]);
  15804 
  15805     var result: struct {
  15806         inst: Air.Inst.Ref = .none,
  15807         wrapped: Value = Value.@"unreachable",
  15808         overflow_bit: Value,
  15809     } = result: {
  15810         switch (zir_tag) {
  15811             .add_with_overflow => {
  15812                 // If either of the arguments is zero, `false` is returned and the other is stored
  15813                 // to the result, even if it is undefined..
  15814                 // Otherwise, if either of the argument is undefined, undefined is returned.
  15815                 if (maybe_lhs_val) |lhs_val| {
  15816                     if (!lhs_val.isUndef(zcu) and (try lhs_val.compareAllWithZeroSema(.eq, pt))) {
  15817                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
  15818                     }
  15819                 }
  15820                 if (maybe_rhs_val) |rhs_val| {
  15821                     if (!rhs_val.isUndef(zcu) and (try rhs_val.compareAllWithZeroSema(.eq, pt))) {
  15822                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15823                     }
  15824                 }
  15825                 if (maybe_lhs_val) |lhs_val| {
  15826                     if (maybe_rhs_val) |rhs_val| {
  15827                         if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
  15828                             break :result .{ .overflow_bit = .undef, .wrapped = .undef };
  15829                         }
  15830 
  15831                         const result = try arith.addWithOverflow(sema, dest_ty, lhs_val, rhs_val);
  15832                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15833                     }
  15834                 }
  15835             },
  15836             .sub_with_overflow => {
  15837                 // If the rhs is zero, then the result is lhs and no overflow occured.
  15838                 // Otherwise, if either result is undefined, both results are undefined.
  15839                 if (maybe_rhs_val) |rhs_val| {
  15840                     if (rhs_val.isUndef(zcu)) {
  15841                         break :result .{ .overflow_bit = .undef, .wrapped = .undef };
  15842                     } else if (try rhs_val.compareAllWithZeroSema(.eq, pt)) {
  15843                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15844                     } else if (maybe_lhs_val) |lhs_val| {
  15845                         if (lhs_val.isUndef(zcu)) {
  15846                             break :result .{ .overflow_bit = .undef, .wrapped = .undef };
  15847                         }
  15848 
  15849                         const result = try arith.subWithOverflow(sema, dest_ty, lhs_val, rhs_val);
  15850                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15851                     }
  15852                 }
  15853             },
  15854             .mul_with_overflow => {
  15855                 // If either of the arguments is zero, the result is zero and no overflow occured.
  15856                 // If either of the arguments is one, the result is the other and no overflow occured.
  15857                 // Otherwise, if either of the arguments is undefined, both results are undefined.
  15858                 const scalar_one = try pt.intValue(dest_ty.scalarType(zcu), 1);
  15859                 if (maybe_lhs_val) |lhs_val| {
  15860                     if (!lhs_val.isUndef(zcu)) {
  15861                         if (try lhs_val.compareAllWithZeroSema(.eq, pt)) {
  15862                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15863                         } else if (try sema.compareAll(lhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) {
  15864                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
  15865                         }
  15866                     }
  15867                 }
  15868 
  15869                 if (maybe_rhs_val) |rhs_val| {
  15870                     if (!rhs_val.isUndef(zcu)) {
  15871                         if (try rhs_val.compareAllWithZeroSema(.eq, pt)) {
  15872                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
  15873                         } else if (try sema.compareAll(rhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) {
  15874                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15875                         }
  15876                     }
  15877                 }
  15878 
  15879                 if (maybe_lhs_val) |lhs_val| {
  15880                     if (maybe_rhs_val) |rhs_val| {
  15881                         if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
  15882                             break :result .{ .overflow_bit = .undef, .wrapped = .undef };
  15883                         }
  15884 
  15885                         const result = try arith.mulWithOverflow(sema, dest_ty, lhs_val, rhs_val);
  15886                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15887                     }
  15888                 }
  15889             },
  15890             .shl_with_overflow => {
  15891                 // If either of the arguments is undefined, IB is possible and we return an error.
  15892                 // If lhs is zero, the result is zero and no overflow occurred.
  15893                 // If rhs is zero, the result is lhs and no overflow occurred.
  15894                 const scalar_ty = lhs_ty.scalarType(zcu);
  15895                 if (maybe_rhs_val) |rhs_val| {
  15896                     if (maybe_lhs_val) |lhs_val| {
  15897                         const result = try arith.shlWithOverflow(sema, block, lhs_ty, lhs_val, rhs_val, lhs_src, rhs_src);
  15898                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15899                     }
  15900                     if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, null);
  15901                     const bits = scalar_ty.intInfo(zcu).bits;
  15902                     switch (rhs_ty.zigTypeTag(zcu)) {
  15903                         .int, .comptime_int => {
  15904                             switch (try rhs_val.orderAgainstZeroSema(pt)) {
  15905                                 .gt => {
  15906                                     var rhs_space: Value.BigIntSpace = undefined;
  15907                                     const rhs_bigint = try rhs_val.toBigIntSema(&rhs_space, pt);
  15908                                     if (rhs_bigint.orderAgainstScalar(bits) != .lt) {
  15909                                         return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null);
  15910                                     }
  15911                                 },
  15912                                 .eq => break :result .{ .overflow_bit = .zero_u1, .inst = lhs },
  15913                                 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null),
  15914                             }
  15915                         },
  15916                         .vector => {
  15917                             var any_positive: bool = false;
  15918                             for (0..rhs_ty.vectorLen(zcu)) |elem_idx| {
  15919                                 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
  15920                                 if (rhs_elem.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, elem_idx);
  15921                                 switch (try rhs_elem.orderAgainstZeroSema(pt)) {
  15922                                     .gt => {
  15923                                         var rhs_elem_space: Value.BigIntSpace = undefined;
  15924                                         const rhs_elem_bigint = try rhs_elem.toBigIntSema(&rhs_elem_space, pt);
  15925                                         if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) {
  15926                                             return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx);
  15927                                         }
  15928                                         any_positive = true;
  15929                                     },
  15930                                     .eq => {},
  15931                                     .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx),
  15932                                 }
  15933                             }
  15934                             if (!any_positive) break :result .{ .overflow_bit = try pt.aggregateSplatValue(overflow_ty, .zero_u1), .inst = lhs };
  15935                         },
  15936                         else => unreachable,
  15937                     }
  15938                     if (try rhs_val.compareAllWithZeroSema(.eq, pt)) {
  15939                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15940                     }
  15941                 } else {
  15942                     if (scalar_ty.toIntern() == .comptime_int_type) {
  15943                         return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
  15944                     }
  15945                     if (maybe_lhs_val) |lhs_val| {
  15946                         try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  15947                         if (try lhs_val.compareAllWithZeroSema(.eq, pt)) {
  15948                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15949                         }
  15950                     }
  15951                 }
  15952             },
  15953             else => unreachable,
  15954         }
  15955 
  15956         const air_tag: Air.Inst.Tag = switch (zir_tag) {
  15957             .add_with_overflow => .add_with_overflow,
  15958             .mul_with_overflow => .mul_with_overflow,
  15959             .sub_with_overflow => .sub_with_overflow,
  15960             .shl_with_overflow => .shl_with_overflow,
  15961             else => unreachable,
  15962         };
  15963 
  15964         return block.addInst(.{
  15965             .tag = air_tag,
  15966             .data = .{ .ty_pl = .{
  15967                 .ty = Air.internedToRef(tuple_ty.toIntern()),
  15968                 .payload = try block.sema.addExtra(Air.Bin{
  15969                     .lhs = lhs,
  15970                     .rhs = rhs,
  15971                 }),
  15972             } },
  15973         });
  15974     };
  15975 
  15976     if (result.inst != .none) {
  15977         if (try sema.resolveValue(result.inst)) |some| {
  15978             result.wrapped = some;
  15979             result.inst = .none;
  15980         }
  15981     }
  15982 
  15983     if (result.inst == .none) {
  15984         return Air.internedToRef((try pt.aggregateValue(tuple_ty, &.{
  15985             result.wrapped.toIntern(),
  15986             result.overflow_bit.toIntern(),
  15987         })).toIntern());
  15988     }
  15989 
  15990     const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2);
  15991     element_refs[0] = result.inst;
  15992     element_refs[1] = Air.internedToRef(result.overflow_bit.toIntern());
  15993     return block.addAggregateInit(tuple_ty, element_refs);
  15994 }
  15995 
  15996 fn splat(sema: *Sema, ty: Type, val: Value) !Value {
  15997     const pt = sema.pt;
  15998     if (ty.zigTypeTag(pt.zcu) != .vector) return val;
  15999     return pt.aggregateSplatValue(ty, val);
  16000 }
  16001 
  16002 fn analyzeArithmetic(
  16003     sema: *Sema,
  16004     block: *Block,
  16005     zir_tag: Zir.Inst.Tag,
  16006     lhs: Air.Inst.Ref,
  16007     rhs: Air.Inst.Ref,
  16008     src: LazySrcLoc,
  16009     lhs_src: LazySrcLoc,
  16010     rhs_src: LazySrcLoc,
  16011     want_safety: bool,
  16012 ) CompileError!Air.Inst.Ref {
  16013     const pt = sema.pt;
  16014     const zcu = pt.zcu;
  16015     const lhs_ty = sema.typeOf(lhs);
  16016     const rhs_ty = sema.typeOf(rhs);
  16017     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  16018     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  16019     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  16020 
  16021     if (lhs_zig_ty_tag == .pointer) {
  16022         if (rhs_zig_ty_tag == .pointer) {
  16023             if (lhs_ty.ptrSize(zcu) != .slice and rhs_ty.ptrSize(zcu) != .slice) {
  16024                 if (zir_tag != .sub) {
  16025                     return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction");
  16026                 }
  16027                 if (!lhs_ty.elemType2(zcu).eql(rhs_ty.elemType2(zcu), zcu)) {
  16028                     return sema.fail(block, src, "incompatible pointer arithmetic operands '{f}' and '{f}'", .{
  16029                         lhs_ty.fmt(pt), rhs_ty.fmt(pt),
  16030                     });
  16031                 }
  16032 
  16033                 const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu);
  16034                 if (elem_size == 0) {
  16035                     return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{
  16036                         lhs_ty.elemType2(zcu).fmt(pt),
  16037                     });
  16038                 }
  16039 
  16040                 const runtime_src = runtime_src: {
  16041                     if (try sema.resolveValue(lhs)) |lhs_value| {
  16042                         if (try sema.resolveValue(rhs)) |rhs_value| {
  16043                             const lhs_ptr = switch (zcu.intern_pool.indexToKey(lhs_value.toIntern())) {
  16044                                 .undef => return sema.failWithUseOfUndef(block, lhs_src, null),
  16045                                 .ptr => |ptr| ptr,
  16046                                 else => unreachable,
  16047                             };
  16048                             const rhs_ptr = switch (zcu.intern_pool.indexToKey(rhs_value.toIntern())) {
  16049                                 .undef => return sema.failWithUseOfUndef(block, rhs_src, null),
  16050                                 .ptr => |ptr| ptr,
  16051                                 else => unreachable,
  16052                             };
  16053                             // Make sure the pointers point to the same data.
  16054                             if (!lhs_ptr.base_addr.eql(rhs_ptr.base_addr)) break :runtime_src src;
  16055                             const address = std.math.sub(u64, lhs_ptr.byte_offset, rhs_ptr.byte_offset) catch
  16056                                 return sema.fail(block, src, "operation results in overflow", .{});
  16057                             const result = address / elem_size;
  16058                             return try pt.intRef(.usize, result);
  16059                         } else {
  16060                             break :runtime_src lhs_src;
  16061                         }
  16062                     } else {
  16063                         break :runtime_src rhs_src;
  16064                     }
  16065                 };
  16066 
  16067                 try sema.requireRuntimeBlock(block, src, runtime_src);
  16068                 try sema.checkLogicalPtrOperation(block, src, lhs_ty);
  16069                 try sema.checkLogicalPtrOperation(block, src, rhs_ty);
  16070                 const lhs_int = try block.addBitCast(.usize, lhs);
  16071                 const rhs_int = try block.addBitCast(.usize, rhs);
  16072                 const address = try block.addBinOp(.sub_wrap, lhs_int, rhs_int);
  16073                 return try block.addBinOp(.div_exact, address, try pt.intRef(.usize, elem_size));
  16074             }
  16075         } else {
  16076             switch (lhs_ty.ptrSize(zcu)) {
  16077                 .one, .slice => {},
  16078                 .many, .c => {
  16079                     const air_tag: Air.Inst.Tag = switch (zir_tag) {
  16080                         .add => .ptr_add,
  16081                         .sub => .ptr_sub,
  16082                         else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
  16083                     };
  16084 
  16085                     if (!try lhs_ty.elemType2(zcu).hasRuntimeBitsSema(pt)) {
  16086                         return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{
  16087                             lhs_ty.elemType2(zcu).fmt(pt),
  16088                         });
  16089                     }
  16090                     return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
  16091                 },
  16092             }
  16093         }
  16094     }
  16095 
  16096     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  16097         .override = &.{ lhs_src, rhs_src },
  16098     });
  16099 
  16100     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  16101     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  16102 
  16103     const scalar_type = resolved_type.scalarType(zcu);
  16104     const scalar_tag = scalar_type.zigTypeTag(zcu);
  16105 
  16106     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag);
  16107 
  16108     // The rules we'll implement below are as follows:
  16109     //
  16110     // * If both operands are comptime-known, we call the corresponding function in `arith` to get
  16111     //   the comptime-known result. Inputs which would be IB at runtime are compile errors.
  16112     //
  16113     // * Otherwise, if one operand is comptime-known `undefined`, we trigger a compile error if this
  16114     //   operator can ever possibly trigger IB, or otherwise return comptime-known `undefined`.
  16115     //
  16116     // * No other comptime operand detemines a comptime result; e.g. `0 * x` isn't always `0` because
  16117     //   of `undefined`. Therefore, the remaining cases all become runtime operations.
  16118 
  16119     const is_int = switch (scalar_tag) {
  16120         .int, .comptime_int => true,
  16121         .float, .comptime_float => false,
  16122         else => unreachable,
  16123     };
  16124 
  16125     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  16126     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  16127 
  16128     if (maybe_lhs_val) |lhs_val| {
  16129         if (maybe_rhs_val) |rhs_val| {
  16130             const result_val = switch (zir_tag) {
  16131                 .add, .add_unsafe => try arith.add(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src),
  16132                 .addwrap => try arith.addWrap(sema, resolved_type, lhs_val, rhs_val),
  16133                 .add_sat => try arith.addSat(sema, resolved_type, lhs_val, rhs_val),
  16134                 .sub => try arith.sub(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src),
  16135                 .subwrap => try arith.subWrap(sema, resolved_type, lhs_val, rhs_val),
  16136                 .sub_sat => try arith.subSat(sema, resolved_type, lhs_val, rhs_val),
  16137                 .mul => try arith.mul(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src),
  16138                 .mulwrap => try arith.mulWrap(sema, resolved_type, lhs_val, rhs_val),
  16139                 .mul_sat => try arith.mulSat(sema, resolved_type, lhs_val, rhs_val),
  16140                 else => unreachable,
  16141             };
  16142             return Air.internedToRef(result_val.toIntern());
  16143         }
  16144     }
  16145 
  16146     const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag, const allow_undef: bool = switch (zir_tag) {
  16147         .add, .add_unsafe => .{ if (block.float_mode == .optimized) .add_optimized else .add, .add_safe, !is_int },
  16148         .addwrap => .{ .add_wrap, .add_wrap, true },
  16149         .add_sat => .{ .add_sat, .add_sat, true },
  16150         .sub => .{ if (block.float_mode == .optimized) .sub_optimized else .sub, .sub_safe, !is_int },
  16151         .subwrap => .{ .sub_wrap, .sub_wrap, true },
  16152         .sub_sat => .{ .sub_sat, .sub_sat, true },
  16153         .mul => .{ if (block.float_mode == .optimized) .mul_optimized else .mul, .mul_safe, !is_int },
  16154         .mulwrap => .{ .mul_wrap, .mul_wrap, true },
  16155         .mul_sat => .{ .mul_sat, .mul_sat, true },
  16156         else => unreachable,
  16157     };
  16158 
  16159     if (allow_undef) {
  16160         if (maybe_lhs_val) |lhs_val| {
  16161             if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  16162         }
  16163         if (maybe_rhs_val) |rhs_val| {
  16164             if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
  16165         }
  16166     } else {
  16167         if (maybe_lhs_val) |lhs_val| {
  16168             try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
  16169         }
  16170         if (maybe_rhs_val) |rhs_val| {
  16171             try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
  16172         }
  16173     }
  16174 
  16175     if (block.wantSafety() and want_safety and scalar_tag == .int) {
  16176         if (air_tag != air_tag_safe) try sema.preparePanicId(src, .integer_overflow);
  16177         return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs);
  16178     }
  16179     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  16180 }
  16181 
  16182 fn analyzePtrArithmetic(
  16183     sema: *Sema,
  16184     block: *Block,
  16185     op_src: LazySrcLoc,
  16186     ptr: Air.Inst.Ref,
  16187     uncasted_offset: Air.Inst.Ref,
  16188     air_tag: Air.Inst.Tag,
  16189     ptr_src: LazySrcLoc,
  16190     offset_src: LazySrcLoc,
  16191 ) CompileError!Air.Inst.Ref {
  16192     // TODO if the operand is comptime-known to be negative, or is a negative int,
  16193     // coerce to isize instead of usize.
  16194     const offset = try sema.coerce(block, .usize, uncasted_offset, offset_src);
  16195     const pt = sema.pt;
  16196     const zcu = pt.zcu;
  16197     const opt_ptr_val = try sema.resolveValue(ptr);
  16198     const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset);
  16199     const ptr_ty = sema.typeOf(ptr);
  16200     const ptr_info = ptr_ty.ptrInfo(zcu);
  16201     assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c);
  16202 
  16203     if ((try sema.typeHasOnePossibleValue(.fromInterned(ptr_info.child))) != null) {
  16204         // Offset will be multiplied by zero, so result is the same as the base pointer.
  16205         return ptr;
  16206     }
  16207 
  16208     const new_ptr_ty = t: {
  16209         // Calculate the new pointer alignment.
  16210         // This code is duplicated in `Type.elemPtrType`.
  16211         if (ptr_info.flags.alignment == .none) {
  16212             // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
  16213             break :t ptr_ty;
  16214         }
  16215         // If the addend is not a comptime-known value we can still count on
  16216         // it being a multiple of the type size.
  16217         const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt);
  16218         const addend = if (opt_off_val) |off_val| a: {
  16219             const off_int = try sema.usizeCast(block, offset_src, try off_val.toUnsignedIntSema(pt));
  16220             break :a elem_size * off_int;
  16221         } else elem_size;
  16222 
  16223         // The resulting pointer is aligned to the lcd between the offset (an
  16224         // arbitrary number) and the alignment factor (always a power of two,
  16225         // non zero).
  16226         const new_align: Alignment = @enumFromInt(@min(
  16227             @ctz(addend),
  16228             @intFromEnum(ptr_info.flags.alignment),
  16229         ));
  16230         assert(new_align != .none);
  16231 
  16232         break :t try pt.ptrTypeSema(.{
  16233             .child = ptr_info.child,
  16234             .sentinel = ptr_info.sentinel,
  16235             .flags = .{
  16236                 .size = ptr_info.flags.size,
  16237                 .alignment = new_align,
  16238                 .is_const = ptr_info.flags.is_const,
  16239                 .is_volatile = ptr_info.flags.is_volatile,
  16240                 .is_allowzero = ptr_info.flags.is_allowzero,
  16241                 .address_space = ptr_info.flags.address_space,
  16242             },
  16243         });
  16244     };
  16245 
  16246     const runtime_src = rs: {
  16247         if (opt_ptr_val) |ptr_val| {
  16248             if (opt_off_val) |offset_val| {
  16249                 if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty);
  16250 
  16251                 const offset_int = try sema.usizeCast(block, offset_src, try offset_val.toUnsignedIntSema(pt));
  16252                 if (offset_int == 0) return ptr;
  16253                 if (air_tag == .ptr_sub) {
  16254                     const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt);
  16255                     const new_ptr_val = try sema.ptrSubtract(block, op_src, ptr_val, offset_int * elem_size, new_ptr_ty);
  16256                     return Air.internedToRef(new_ptr_val.toIntern());
  16257                 } else {
  16258                     const new_ptr_val = try pt.getCoerced(try ptr_val.ptrElem(offset_int, pt), new_ptr_ty);
  16259                     return Air.internedToRef(new_ptr_val.toIntern());
  16260                 }
  16261             } else break :rs offset_src;
  16262         } else break :rs ptr_src;
  16263     };
  16264 
  16265     try sema.requireRuntimeBlock(block, op_src, runtime_src);
  16266     try sema.checkLogicalPtrOperation(block, op_src, ptr_ty);
  16267 
  16268     return block.addInst(.{
  16269         .tag = air_tag,
  16270         .data = .{ .ty_pl = .{
  16271             .ty = Air.internedToRef(new_ptr_ty.toIntern()),
  16272             .payload = try sema.addExtra(Air.Bin{
  16273                 .lhs = ptr,
  16274                 .rhs = offset,
  16275             }),
  16276         } },
  16277     });
  16278 }
  16279 
  16280 fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16281     const tracy = trace(@src());
  16282     defer tracy.end();
  16283 
  16284     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16285     const src = block.nodeOffset(inst_data.src_node);
  16286     const ptr_src = src; // TODO better source location
  16287     const ptr = try sema.resolveInst(inst_data.operand);
  16288     return sema.analyzeLoad(block, src, ptr, ptr_src);
  16289 }
  16290 
  16291 fn zirAsm(
  16292     sema: *Sema,
  16293     block: *Block,
  16294     extended: Zir.Inst.Extended.InstData,
  16295     tmpl_is_expr: bool,
  16296 ) CompileError!Air.Inst.Ref {
  16297     const tracy = trace(@src());
  16298     defer tracy.end();
  16299 
  16300     const pt = sema.pt;
  16301     const zcu = pt.zcu;
  16302     const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
  16303     const src = block.nodeOffset(extra.data.src_node);
  16304     const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node });
  16305     const small: Zir.Inst.Asm.Small = @bitCast(extended.small);
  16306     const outputs_len = small.outputs_len;
  16307     const inputs_len = small.inputs_len;
  16308     const is_volatile = small.is_volatile;
  16309     const is_global_assembly = sema.func_index == .none;
  16310     const zir_tags = sema.code.instructions.items(.tag);
  16311 
  16312     const asm_source: []const u8 = if (tmpl_is_expr) s: {
  16313         const tmpl: Zir.Inst.Ref = @enumFromInt(@intFromEnum(extra.data.asm_source));
  16314         break :s try sema.resolveConstString(block, src, tmpl, .{ .simple = .inline_assembly_code });
  16315     } else sema.code.nullTerminatedString(extra.data.asm_source);
  16316 
  16317     if (is_global_assembly) {
  16318         if (outputs_len != 0) {
  16319             return sema.fail(block, src, "module-level assembly does not support outputs", .{});
  16320         }
  16321         if (inputs_len != 0) {
  16322             return sema.fail(block, src, "module-level assembly does not support inputs", .{});
  16323         }
  16324         if (extra.data.clobbers != .none) {
  16325             return sema.fail(block, src, "module-level assembly does not support clobbers", .{});
  16326         }
  16327         if (is_volatile) {
  16328             return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{});
  16329         }
  16330         try zcu.addGlobalAssembly(sema.owner, asm_source);
  16331         return .void_value;
  16332     }
  16333 
  16334     try sema.requireRuntimeBlock(block, src, null);
  16335 
  16336     var extra_i = extra.end;
  16337     var output_type_bits = extra.data.output_type_bits;
  16338     var needed_capacity: usize = @typeInfo(Air.Asm).@"struct".fields.len + outputs_len + inputs_len;
  16339 
  16340     const ConstraintName = struct { c: []const u8, n: []const u8 };
  16341     const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len);
  16342     const outputs = try sema.arena.alloc(ConstraintName, outputs_len);
  16343     var expr_ty = Air.Inst.Ref.void_type;
  16344 
  16345     for (out_args, 0..) |*arg, out_i| {
  16346         const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);
  16347         extra_i = output.end;
  16348 
  16349         const is_type = @as(u1, @truncate(output_type_bits)) != 0;
  16350         output_type_bits >>= 1;
  16351 
  16352         if (is_type) {
  16353             // Indicate the output is the asm instruction return value.
  16354             arg.* = .none;
  16355             const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand);
  16356             expr_ty = Air.internedToRef(out_ty.toIntern());
  16357         } else {
  16358             arg.* = try sema.resolveInst(output.data.operand);
  16359         }
  16360 
  16361         const constraint = sema.code.nullTerminatedString(output.data.constraint);
  16362         const name = sema.code.nullTerminatedString(output.data.name);
  16363         needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
  16364 
  16365         if (output.data.operand.toIndex()) |index| {
  16366             if (zir_tags[@intFromEnum(index)] == .ref) {
  16367                 // TODO: better error location; it would be even nicer if there were notes that pointed at the output and the variable definition
  16368                 return sema.fail(block, src, "asm cannot output to const local '{s}'", .{name});
  16369             }
  16370         }
  16371 
  16372         outputs[out_i] = .{ .c = constraint, .n = name };
  16373     }
  16374 
  16375     const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len);
  16376     const inputs = try sema.arena.alloc(ConstraintName, inputs_len);
  16377 
  16378     for (args, 0..) |*arg, arg_i| {
  16379         const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i);
  16380         extra_i = input.end;
  16381 
  16382         const uncasted_arg = try sema.resolveInst(input.data.operand);
  16383         const uncasted_arg_ty = sema.typeOf(uncasted_arg);
  16384         switch (uncasted_arg_ty.zigTypeTag(zcu)) {
  16385             .comptime_int => arg.* = try sema.coerce(block, .usize, uncasted_arg, src),
  16386             .comptime_float => arg.* = try sema.coerce(block, .f64, uncasted_arg, src),
  16387             else => {
  16388                 arg.* = uncasted_arg;
  16389             },
  16390         }
  16391 
  16392         const constraint = sema.code.nullTerminatedString(input.data.constraint);
  16393         const name = sema.code.nullTerminatedString(input.data.name);
  16394         needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
  16395         inputs[arg_i] = .{ .c = constraint, .n = name };
  16396     }
  16397 
  16398     const clobbers = if (extra.data.clobbers == .none) empty: {
  16399         const clobbers_ty = try sema.getBuiltinType(src, .@"assembly.Clobbers");
  16400         break :empty try sema.structInitEmpty(block, clobbers_ty, src, src);
  16401     } else try sema.resolveInst(extra.data.clobbers); // Already coerced by AstGen.
  16402     const clobbers_val = try sema.resolveConstDefinedValue(block, src, clobbers, .{ .simple = .clobber });
  16403     needed_capacity += asm_source.len / 4 + 1;
  16404 
  16405     const gpa = sema.gpa;
  16406     try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
  16407     const asm_air = try block.addInst(.{
  16408         .tag = .assembly,
  16409         .data = .{ .ty_pl = .{
  16410             .ty = expr_ty,
  16411             .payload = sema.addExtraAssumeCapacity(Air.Asm{
  16412                 .source_len = @intCast(asm_source.len),
  16413                 .inputs_len = @intCast(args.len),
  16414                 .clobbers = clobbers_val.toIntern(),
  16415                 .flags = .{
  16416                     .is_volatile = is_volatile,
  16417                     .outputs_len = outputs_len,
  16418                 },
  16419             }),
  16420         } },
  16421     });
  16422     sema.appendRefsAssumeCapacity(out_args);
  16423     sema.appendRefsAssumeCapacity(args);
  16424     for (outputs) |o| {
  16425         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16426         @memcpy(buffer[0..o.c.len], o.c);
  16427         buffer[o.c.len] = 0;
  16428         @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n);
  16429         buffer[o.c.len + 1 + o.n.len] = 0;
  16430         sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4;
  16431     }
  16432     for (inputs) |input| {
  16433         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16434         @memcpy(buffer[0..input.c.len], input.c);
  16435         buffer[input.c.len] = 0;
  16436         @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n);
  16437         buffer[input.c.len + 1 + input.n.len] = 0;
  16438         sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4;
  16439     }
  16440     {
  16441         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16442         @memcpy(buffer[0..asm_source.len], asm_source);
  16443         buffer[asm_source.len] = 0;
  16444         sema.air_extra.items.len += asm_source.len / 4 + 1;
  16445     }
  16446     return asm_air;
  16447 }
  16448 
  16449 /// Only called for equality operators. See also `zirCmp`.
  16450 fn zirCmpEq(
  16451     sema: *Sema,
  16452     block: *Block,
  16453     inst: Zir.Inst.Index,
  16454     op: std.math.CompareOperator,
  16455     air_tag: Air.Inst.Tag,
  16456 ) CompileError!Air.Inst.Ref {
  16457     const tracy = trace(@src());
  16458     defer tracy.end();
  16459 
  16460     const pt = sema.pt;
  16461     const zcu = pt.zcu;
  16462     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  16463     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  16464     const src: LazySrcLoc = block.nodeOffset(inst_data.src_node);
  16465     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  16466     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  16467     const lhs = try sema.resolveInst(extra.lhs);
  16468     const rhs = try sema.resolveInst(extra.rhs);
  16469 
  16470     const lhs_ty = sema.typeOf(lhs);
  16471     const rhs_ty = sema.typeOf(rhs);
  16472     const lhs_ty_tag = lhs_ty.zigTypeTag(zcu);
  16473     const rhs_ty_tag = rhs_ty.zigTypeTag(zcu);
  16474     if (lhs_ty_tag == .null and rhs_ty_tag == .null) {
  16475         // null == null, null != null
  16476         return if (op == .eq) .bool_true else .bool_false;
  16477     }
  16478 
  16479     // comparing null with optionals
  16480     if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) {
  16481         return sema.analyzeIsNull(block, rhs, op == .neq);
  16482     }
  16483     if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) {
  16484         return sema.analyzeIsNull(block, lhs, op == .neq);
  16485     }
  16486 
  16487     if (lhs_ty_tag == .null or rhs_ty_tag == .null) {
  16488         const non_null_type = if (lhs_ty_tag == .null) rhs_ty else lhs_ty;
  16489         return sema.fail(block, src, "comparison of '{f}' with null", .{non_null_type.fmt(pt)});
  16490     }
  16491 
  16492     if (lhs_ty_tag == .@"union" and (rhs_ty_tag == .enum_literal or rhs_ty_tag == .@"enum")) {
  16493         return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op);
  16494     }
  16495     if (rhs_ty_tag == .@"union" and (lhs_ty_tag == .enum_literal or lhs_ty_tag == .@"enum")) {
  16496         return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op);
  16497     }
  16498 
  16499     if (lhs_ty_tag == .error_set and rhs_ty_tag == .error_set) {
  16500         const runtime_src: LazySrcLoc = src: {
  16501             if (try sema.resolveValue(lhs)) |lval| {
  16502                 if (try sema.resolveValue(rhs)) |rval| {
  16503                     if (lval.isUndef(zcu) or rval.isUndef(zcu)) return .undef_bool;
  16504                     const lkey = zcu.intern_pool.indexToKey(lval.toIntern());
  16505                     const rkey = zcu.intern_pool.indexToKey(rval.toIntern());
  16506                     return if ((lkey.err.name == rkey.err.name) == (op == .eq))
  16507                         .bool_true
  16508                     else
  16509                         .bool_false;
  16510                 } else {
  16511                     break :src rhs_src;
  16512                 }
  16513             } else {
  16514                 break :src lhs_src;
  16515             }
  16516         };
  16517         try sema.requireRuntimeBlock(block, src, runtime_src);
  16518         return block.addBinOp(air_tag, lhs, rhs);
  16519     }
  16520     if (lhs_ty_tag == .type and rhs_ty_tag == .type) {
  16521         const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs);
  16522         const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs);
  16523         return if (lhs_as_type.eql(rhs_as_type, zcu) == (op == .eq)) .bool_true else .bool_false;
  16524     }
  16525     return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true);
  16526 }
  16527 
  16528 fn analyzeCmpUnionTag(
  16529     sema: *Sema,
  16530     block: *Block,
  16531     src: LazySrcLoc,
  16532     un: Air.Inst.Ref,
  16533     un_src: LazySrcLoc,
  16534     tag: Air.Inst.Ref,
  16535     tag_src: LazySrcLoc,
  16536     op: std.math.CompareOperator,
  16537 ) CompileError!Air.Inst.Ref {
  16538     const pt = sema.pt;
  16539     const zcu = pt.zcu;
  16540     const union_ty = sema.typeOf(un);
  16541     try union_ty.resolveFields(pt);
  16542     const union_tag_ty = union_ty.unionTagType(zcu) orelse {
  16543         const msg = msg: {
  16544             const msg = try sema.errMsg(un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
  16545             errdefer msg.destroy(sema.gpa);
  16546             try sema.errNote(union_ty.srcLoc(zcu), msg, "union '{f}' is not a tagged union", .{union_ty.fmt(pt)});
  16547             break :msg msg;
  16548         };
  16549         return sema.failWithOwnedErrorMsg(block, msg);
  16550     };
  16551     // Coerce both the union and the tag to the union's tag type, and then execute the
  16552     // enum comparison codepath.
  16553     const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src);
  16554     const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src);
  16555 
  16556     if (try sema.resolveValue(coerced_tag)) |enum_val| {
  16557         if (enum_val.isUndef(zcu)) return .undef_bool;
  16558         const field_ty = union_ty.unionFieldType(enum_val, zcu).?;
  16559         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  16560             return .bool_false;
  16561         }
  16562     }
  16563 
  16564     return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src);
  16565 }
  16566 
  16567 /// Only called for non-equality operators. See also `zirCmpEq`.
  16568 fn zirCmp(
  16569     sema: *Sema,
  16570     block: *Block,
  16571     inst: Zir.Inst.Index,
  16572     op: std.math.CompareOperator,
  16573 ) CompileError!Air.Inst.Ref {
  16574     const tracy = trace(@src());
  16575     defer tracy.end();
  16576 
  16577     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  16578     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  16579     const src: LazySrcLoc = block.nodeOffset(inst_data.src_node);
  16580     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  16581     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  16582     const lhs = try sema.resolveInst(extra.lhs);
  16583     const rhs = try sema.resolveInst(extra.rhs);
  16584     return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false);
  16585 }
  16586 
  16587 fn analyzeCmp(
  16588     sema: *Sema,
  16589     block: *Block,
  16590     src: LazySrcLoc,
  16591     lhs: Air.Inst.Ref,
  16592     rhs: Air.Inst.Ref,
  16593     op: std.math.CompareOperator,
  16594     lhs_src: LazySrcLoc,
  16595     rhs_src: LazySrcLoc,
  16596     is_equality_cmp: bool,
  16597 ) CompileError!Air.Inst.Ref {
  16598     const pt = sema.pt;
  16599     const zcu = pt.zcu;
  16600     const lhs_ty = sema.typeOf(lhs);
  16601     const rhs_ty = sema.typeOf(rhs);
  16602     if (lhs_ty.zigTypeTag(zcu) != .optional and rhs_ty.zigTypeTag(zcu) != .optional) {
  16603         try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  16604     }
  16605 
  16606     if (lhs_ty.zigTypeTag(zcu) == .vector and rhs_ty.zigTypeTag(zcu) == .vector) {
  16607         return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src);
  16608     }
  16609     if (lhs_ty.isNumeric(zcu) and rhs_ty.isNumeric(zcu)) {
  16610         // This operation allows any combination of integer and float types, regardless of the
  16611         // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
  16612         // numeric types.
  16613         return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src);
  16614     }
  16615     if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_union and rhs_ty.zigTypeTag(zcu) == .error_set) {
  16616         if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
  16617             if (lhs_val.errorUnionIsPayload(zcu)) return .bool_false;
  16618         }
  16619         const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs);
  16620         return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src);
  16621     }
  16622     if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_set and rhs_ty.zigTypeTag(zcu) == .error_union) {
  16623         if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
  16624             if (rhs_val.errorUnionIsPayload(zcu)) return .bool_false;
  16625         }
  16626         const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs);
  16627         return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src);
  16628     }
  16629     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  16630     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
  16631     if (!resolved_type.isSelfComparable(zcu, is_equality_cmp)) {
  16632         return sema.fail(block, src, "operator {s} not allowed for type '{f}'", .{
  16633             compareOperatorName(op), resolved_type.fmt(pt),
  16634         });
  16635     }
  16636     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  16637     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  16638     return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src);
  16639 }
  16640 
  16641 fn compareOperatorName(comp: std.math.CompareOperator) []const u8 {
  16642     return switch (comp) {
  16643         .lt => "<",
  16644         .lte => "<=",
  16645         .eq => "==",
  16646         .gte => ">=",
  16647         .gt => ">",
  16648         .neq => "!=",
  16649     };
  16650 }
  16651 
  16652 fn cmpSelf(
  16653     sema: *Sema,
  16654     block: *Block,
  16655     src: LazySrcLoc,
  16656     casted_lhs: Air.Inst.Ref,
  16657     casted_rhs: Air.Inst.Ref,
  16658     op: std.math.CompareOperator,
  16659     lhs_src: LazySrcLoc,
  16660     rhs_src: LazySrcLoc,
  16661 ) CompileError!Air.Inst.Ref {
  16662     const pt = sema.pt;
  16663     const zcu = pt.zcu;
  16664     const resolved_type = sema.typeOf(casted_lhs);
  16665 
  16666     const maybe_lhs_val = try sema.resolveValue(casted_lhs);
  16667     const maybe_rhs_val = try sema.resolveValue(casted_rhs);
  16668     if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
  16669     if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
  16670 
  16671     const runtime_src: LazySrcLoc = src: {
  16672         if (maybe_lhs_val) |lhs_val| {
  16673             if (maybe_rhs_val) |rhs_val| {
  16674                 if (resolved_type.zigTypeTag(zcu) == .vector) {
  16675                     const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);
  16676                     return Air.internedToRef(cmp_val.toIntern());
  16677                 }
  16678 
  16679                 return if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type))
  16680                     .bool_true
  16681                 else
  16682                     .bool_false;
  16683             } else {
  16684                 if (resolved_type.zigTypeTag(zcu) == .bool) {
  16685                     // We can lower bool eq/neq more efficiently.
  16686                     return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src);
  16687                 }
  16688                 break :src rhs_src;
  16689             }
  16690         } else {
  16691             // For bools, we still check the other operand, because we can lower
  16692             // bool eq/neq more efficiently.
  16693             if (resolved_type.zigTypeTag(zcu) == .bool) {
  16694                 if (maybe_rhs_val) |rhs_val| {
  16695                     return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
  16696                 }
  16697             }
  16698             break :src lhs_src;
  16699         }
  16700     };
  16701     try sema.requireRuntimeBlock(block, src, runtime_src);
  16702     if (resolved_type.zigTypeTag(zcu) == .vector) {
  16703         return block.addCmpVector(casted_lhs, casted_rhs, op);
  16704     }
  16705     const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized);
  16706     return block.addBinOp(tag, casted_lhs, casted_rhs);
  16707 }
  16708 
  16709 /// cmp_eq (x, false) => not(x)
  16710 /// cmp_eq (x, true ) => x
  16711 /// cmp_neq(x, false) => x
  16712 /// cmp_neq(x, true ) => not(x)
  16713 fn runtimeBoolCmp(
  16714     sema: *Sema,
  16715     block: *Block,
  16716     src: LazySrcLoc,
  16717     op: std.math.CompareOperator,
  16718     lhs: Air.Inst.Ref,
  16719     rhs: bool,
  16720     runtime_src: LazySrcLoc,
  16721 ) CompileError!Air.Inst.Ref {
  16722     if ((op == .neq) == rhs) {
  16723         try sema.requireRuntimeBlock(block, src, runtime_src);
  16724         return block.addTyOp(.not, .bool, lhs);
  16725     } else {
  16726         return lhs;
  16727     }
  16728 }
  16729 
  16730 fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16731     const pt = sema.pt;
  16732     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16733     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  16734     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
  16735     switch (ty.zigTypeTag(pt.zcu)) {
  16736         .@"fn",
  16737         .noreturn,
  16738         .undefined,
  16739         .null,
  16740         .@"opaque",
  16741         => return sema.fail(block, operand_src, "no size available for type '{f}'", .{ty.fmt(pt)}),
  16742 
  16743         .type,
  16744         .enum_literal,
  16745         .comptime_float,
  16746         .comptime_int,
  16747         .void,
  16748         => return .zero,
  16749 
  16750         .bool,
  16751         .int,
  16752         .float,
  16753         .pointer,
  16754         .array,
  16755         .@"struct",
  16756         .optional,
  16757         .error_union,
  16758         .error_set,
  16759         .@"enum",
  16760         .@"union",
  16761         .vector,
  16762         .frame,
  16763         .@"anyframe",
  16764         => {},
  16765     }
  16766     const val = try ty.abiSizeLazy(pt);
  16767     return Air.internedToRef(val.toIntern());
  16768 }
  16769 
  16770 fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16771     const pt = sema.pt;
  16772     const zcu = pt.zcu;
  16773     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16774     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  16775     const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
  16776     switch (operand_ty.zigTypeTag(zcu)) {
  16777         .@"fn",
  16778         .noreturn,
  16779         .undefined,
  16780         .null,
  16781         .@"opaque",
  16782         => return sema.fail(block, operand_src, "no size available for type '{f}'", .{operand_ty.fmt(pt)}),
  16783 
  16784         .type,
  16785         .enum_literal,
  16786         .comptime_float,
  16787         .comptime_int,
  16788         .void,
  16789         => return .zero,
  16790 
  16791         .bool,
  16792         .int,
  16793         .float,
  16794         .pointer,
  16795         .array,
  16796         .@"struct",
  16797         .optional,
  16798         .error_union,
  16799         .error_set,
  16800         .@"enum",
  16801         .@"union",
  16802         .vector,
  16803         .frame,
  16804         .@"anyframe",
  16805         => {},
  16806     }
  16807     const bit_size = try operand_ty.bitSizeSema(pt);
  16808     return pt.intRef(.comptime_int, bit_size);
  16809 }
  16810 
  16811 fn zirThis(
  16812     sema: *Sema,
  16813     block: *Block,
  16814     extended: Zir.Inst.Extended.InstData,
  16815 ) CompileError!Air.Inst.Ref {
  16816     _ = extended;
  16817     const pt = sema.pt;
  16818     const zcu = pt.zcu;
  16819     const namespace = pt.zcu.namespacePtr(block.namespace);
  16820 
  16821     switch (pt.zcu.intern_pool.indexToKey(namespace.owner_type)) {
  16822         .opaque_type => {
  16823             // Opaque types are never outdated since they don't undergo type resolution, so nothing to do!
  16824             return Air.internedToRef(namespace.owner_type);
  16825         },
  16826         .struct_type, .union_type => {
  16827             const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
  16828             try sema.declareDependency(.{ .interned = new_ty });
  16829             return Air.internedToRef(new_ty);
  16830         },
  16831         .enum_type => {
  16832             const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
  16833             try sema.declareDependency(.{ .interned = new_ty });
  16834             // Since this is an enum, it has to be resolved immediately.
  16835             // `ensureTypeUpToDate` has resolved the new type if necessary.
  16836             // We just need to check for resolution failures.
  16837             const ty_unit: AnalUnit = .wrap(.{ .type = new_ty });
  16838             if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) {
  16839                 return error.AnalysisFail;
  16840             }
  16841             return Air.internedToRef(new_ty);
  16842         },
  16843         else => unreachable,
  16844     }
  16845 }
  16846 
  16847 fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  16848     const pt = sema.pt;
  16849     const zcu = pt.zcu;
  16850     const ip = &zcu.intern_pool;
  16851     const captures = Type.fromInterned(zcu.namespacePtr(block.namespace).owner_type).getCaptures(zcu);
  16852 
  16853     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  16854     const src = block.nodeOffset(src_node);
  16855 
  16856     const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) {
  16857         .@"comptime" => |index| return Air.internedToRef(index),
  16858         .runtime => |index| index,
  16859         .nav_val => |nav| return sema.analyzeNavVal(block, src, nav),
  16860         .nav_ref => |nav| return sema.analyzeNavRef(block, src, nav),
  16861     };
  16862 
  16863     // The comptime case is handled already above. Runtime case below.
  16864 
  16865     if (!block.is_typeof and sema.func_index == .none) {
  16866         const msg = msg: {
  16867             const name = name: {
  16868                 // TODO: we should probably store this name in the ZIR to avoid this complexity.
  16869                 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
  16870                 const tree = file.getTree(zcu) catch |err| {
  16871                     // In this case we emit a warning + a less precise source location.
  16872                     log.warn("unable to load {f}: {s}", .{
  16873                         file.path.fmt(zcu.comp), @errorName(err),
  16874                     });
  16875                     break :name null;
  16876                 };
  16877                 const node = src_node.toAbsolute(src_base_node);
  16878                 const token = tree.nodeMainToken(node);
  16879                 break :name tree.tokenSlice(token);
  16880             };
  16881 
  16882             const msg = if (name) |some|
  16883                 try sema.errMsg(src, "'{s}' not accessible outside function scope", .{some})
  16884             else
  16885                 try sema.errMsg(src, "variable not accessible outside function scope", .{});
  16886             errdefer msg.destroy(sema.gpa);
  16887 
  16888             // TODO add "declared here" note
  16889             break :msg msg;
  16890         };
  16891         return sema.failWithOwnedErrorMsg(block, msg);
  16892     }
  16893 
  16894     if (!block.is_typeof and !block.isComptime() and sema.func_index != .none) {
  16895         const msg = msg: {
  16896             const name = name: {
  16897                 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
  16898                 const tree = file.getTree(zcu) catch |err| {
  16899                     // In this case we emit a warning + a less precise source location.
  16900                     log.warn("unable to load {f}: {s}", .{
  16901                         file.path.fmt(zcu.comp), @errorName(err),
  16902                     });
  16903                     break :name null;
  16904                 };
  16905                 const node = src_node.toAbsolute(src_base_node);
  16906                 const token = tree.nodeMainToken(node);
  16907                 break :name tree.tokenSlice(token);
  16908             };
  16909 
  16910             const msg = if (name) |some|
  16911                 try sema.errMsg(src, "'{s}' not accessible from inner function", .{some})
  16912             else
  16913                 try sema.errMsg(src, "variable not accessible from inner function", .{});
  16914             errdefer msg.destroy(sema.gpa);
  16915 
  16916             try sema.errNote(block.nodeOffset(.zero), msg, "crossed function definition here", .{});
  16917 
  16918             // TODO add "declared here" note
  16919             break :msg msg;
  16920         };
  16921         return sema.failWithOwnedErrorMsg(block, msg);
  16922     }
  16923 
  16924     assert(block.is_typeof);
  16925     // We need a dummy runtime instruction with the correct type.
  16926     return block.addTy(.alloc, .fromInterned(capture_ty));
  16927 }
  16928 
  16929 fn zirRetAddr(
  16930     sema: *Sema,
  16931     block: *Block,
  16932     extended: Zir.Inst.Extended.InstData,
  16933 ) CompileError!Air.Inst.Ref {
  16934     _ = sema;
  16935     _ = extended;
  16936     if (block.isComptime()) {
  16937         // TODO: we could give a meaningful lazy value here. #14938
  16938         return .zero_usize;
  16939     } else {
  16940         return block.addNoOp(.ret_addr);
  16941     }
  16942 }
  16943 
  16944 fn zirFrameAddress(
  16945     sema: *Sema,
  16946     block: *Block,
  16947     extended: Zir.Inst.Extended.InstData,
  16948 ) CompileError!Air.Inst.Ref {
  16949     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  16950     const src = block.nodeOffset(src_node);
  16951     try sema.requireRuntimeBlock(block, src, null);
  16952     return try block.addNoOp(.frame_addr);
  16953 }
  16954 
  16955 fn zirBuiltinSrc(
  16956     sema: *Sema,
  16957     block: *Block,
  16958     extended: Zir.Inst.Extended.InstData,
  16959 ) CompileError!Air.Inst.Ref {
  16960     const tracy = trace(@src());
  16961     defer tracy.end();
  16962 
  16963     const pt = sema.pt;
  16964     const zcu = pt.zcu;
  16965     const ip = &zcu.intern_pool;
  16966     const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data;
  16967     const fn_name = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).name;
  16968     const gpa = sema.gpa;
  16969     const file_scope = block.getFileScope(zcu);
  16970 
  16971     const func_name_val = v: {
  16972         const func_name_len = fn_name.length(ip);
  16973         const array_ty = try pt.intern(.{ .array_type = .{
  16974             .len = func_name_len,
  16975             .sentinel = .zero_u8,
  16976             .child = .u8_type,
  16977         } });
  16978         break :v try pt.intern(.{ .slice = .{
  16979             .ty = .slice_const_u8_sentinel_0_type,
  16980             .ptr = try pt.intern(.{ .ptr = .{
  16981                 .ty = .manyptr_const_u8_sentinel_0_type,
  16982                 .base_addr = .{ .uav = .{
  16983                     .orig_ty = .slice_const_u8_sentinel_0_type,
  16984                     .val = try pt.intern(.{ .aggregate = .{
  16985                         .ty = array_ty,
  16986                         .storage = .{ .bytes = fn_name.toString() },
  16987                     } }),
  16988                 } },
  16989                 .byte_offset = 0,
  16990             } }),
  16991             .len = (try pt.intValue(.usize, func_name_len)).toIntern(),
  16992         } });
  16993     };
  16994 
  16995     const module_name_val = v: {
  16996         const module_name = file_scope.mod.?.fully_qualified_name;
  16997         const array_ty = try pt.intern(.{ .array_type = .{
  16998             .len = module_name.len,
  16999             .sentinel = .zero_u8,
  17000             .child = .u8_type,
  17001         } });
  17002         break :v try pt.intern(.{ .slice = .{
  17003             .ty = .slice_const_u8_sentinel_0_type,
  17004             .ptr = try pt.intern(.{ .ptr = .{
  17005                 .ty = .manyptr_const_u8_sentinel_0_type,
  17006                 .base_addr = .{ .uav = .{
  17007                     .orig_ty = .slice_const_u8_sentinel_0_type,
  17008                     .val = try pt.intern(.{ .aggregate = .{
  17009                         .ty = array_ty,
  17010                         .storage = .{
  17011                             .bytes = try ip.getOrPutString(gpa, pt.tid, module_name, .maybe_embedded_nulls),
  17012                         },
  17013                     } }),
  17014                 } },
  17015                 .byte_offset = 0,
  17016             } }),
  17017             .len = (try pt.intValue(.usize, module_name.len)).toIntern(),
  17018         } });
  17019     };
  17020 
  17021     const file_name_val = v: {
  17022         const file_name = file_scope.sub_file_path;
  17023         const array_ty = try pt.intern(.{ .array_type = .{
  17024             .len = file_name.len,
  17025             .sentinel = .zero_u8,
  17026             .child = .u8_type,
  17027         } });
  17028         break :v try pt.intern(.{ .slice = .{
  17029             .ty = .slice_const_u8_sentinel_0_type,
  17030             .ptr = try pt.intern(.{ .ptr = .{
  17031                 .ty = .manyptr_const_u8_sentinel_0_type,
  17032                 .base_addr = .{ .uav = .{
  17033                     .orig_ty = .slice_const_u8_sentinel_0_type,
  17034                     .val = try pt.intern(.{ .aggregate = .{
  17035                         .ty = array_ty,
  17036                         .storage = .{
  17037                             .bytes = try ip.getOrPutString(gpa, pt.tid, file_name, .maybe_embedded_nulls),
  17038                         },
  17039                     } }),
  17040                 } },
  17041                 .byte_offset = 0,
  17042             } }),
  17043             .len = (try pt.intValue(.usize, file_name.len)).toIntern(),
  17044         } });
  17045     };
  17046 
  17047     const src_loc_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .SourceLocation);
  17048     const fields = .{
  17049         // module: [:0]const u8,
  17050         module_name_val,
  17051         // file: [:0]const u8,
  17052         file_name_val,
  17053         // fn_name: [:0]const u8,
  17054         func_name_val,
  17055         // line: u32,
  17056         (try pt.intValue(.u32, extra.line + 1)).toIntern(),
  17057         // column: u32,
  17058         (try pt.intValue(.u32, extra.column + 1)).toIntern(),
  17059     };
  17060     return Air.internedToRef((try pt.aggregateValue(src_loc_ty, &fields)).toIntern());
  17061 }
  17062 
  17063 fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17064     const pt = sema.pt;
  17065     const zcu = pt.zcu;
  17066     const gpa = sema.gpa;
  17067     const ip = &zcu.intern_pool;
  17068     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  17069     const src = block.nodeOffset(inst_data.src_node);
  17070     const ty = try sema.resolveType(block, src, inst_data.operand);
  17071     const type_info_ty = try sema.getBuiltinType(src, .Type);
  17072     const type_info_tag_ty = type_info_ty.unionTagType(zcu).?;
  17073 
  17074     if (ty.typeDeclInst(zcu)) |type_decl_inst| {
  17075         try sema.declareDependency(.{ .namespace = type_decl_inst });
  17076     }
  17077 
  17078     switch (ty.zigTypeTag(zcu)) {
  17079         .type,
  17080         .void,
  17081         .bool,
  17082         .noreturn,
  17083         .comptime_float,
  17084         .comptime_int,
  17085         .undefined,
  17086         .null,
  17087         .enum_literal,
  17088         => |type_info_tag| return unionInitFromEnumTag(sema, block, src, type_info_ty, @intFromEnum(type_info_tag), .void_value),
  17089 
  17090         .@"fn" => {
  17091             const fn_info_ty = try sema.getBuiltinType(src, .@"Type.Fn");
  17092             const param_info_ty = try sema.getBuiltinType(src, .@"Type.Fn.Param");
  17093 
  17094             const func_ty_info = zcu.typeToFunc(ty).?;
  17095             const param_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);
  17096             for (param_vals, 0..) |*param_val, i| {
  17097                 const param_ty = func_ty_info.param_types.get(ip)[i];
  17098                 const is_generic = param_ty == .generic_poison_type;
  17099                 const param_ty_val = try pt.intern(.{ .opt = .{
  17100                     .ty = try pt.intern(.{ .opt_type = .type_type }),
  17101                     .val = if (is_generic) .none else param_ty,
  17102                 } });
  17103 
  17104                 const is_noalias = blk: {
  17105                     const index = std.math.cast(u5, i) orelse break :blk false;
  17106                     break :blk @as(u1, @truncate(func_ty_info.noalias_bits >> index)) != 0;
  17107                 };
  17108 
  17109                 const param_fields = .{
  17110                     // is_generic: bool,
  17111                     Value.makeBool(is_generic).toIntern(),
  17112                     // is_noalias: bool,
  17113                     Value.makeBool(is_noalias).toIntern(),
  17114                     // type: ?type,
  17115                     param_ty_val,
  17116                 };
  17117                 param_val.* = (try pt.aggregateValue(param_info_ty, &param_fields)).toIntern();
  17118             }
  17119 
  17120             const args_val = v: {
  17121                 const new_decl_ty = try pt.arrayType(.{
  17122                     .len = param_vals.len,
  17123                     .child = param_info_ty.toIntern(),
  17124                 });
  17125                 const new_decl_val = (try pt.aggregateValue(new_decl_ty, param_vals)).toIntern();
  17126                 const slice_ty = (try pt.ptrTypeSema(.{
  17127                     .child = param_info_ty.toIntern(),
  17128                     .flags = .{
  17129                         .size = .slice,
  17130                         .is_const = true,
  17131                     },
  17132                 })).toIntern();
  17133                 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  17134                 break :v try pt.intern(.{ .slice = .{
  17135                     .ty = slice_ty,
  17136                     .ptr = try pt.intern(.{ .ptr = .{
  17137                         .ty = manyptr_ty,
  17138                         .base_addr = .{ .uav = .{
  17139                             .orig_ty = manyptr_ty,
  17140                             .val = new_decl_val,
  17141                         } },
  17142                         .byte_offset = 0,
  17143                     } }),
  17144                     .len = (try pt.intValue(.usize, param_vals.len)).toIntern(),
  17145                 } });
  17146             };
  17147 
  17148             const ret_ty_opt = try pt.intern(.{ .opt = .{
  17149                 .ty = try pt.intern(.{ .opt_type = .type_type }),
  17150                 .val = opt_val: {
  17151                     const ret_ty: Type = .fromInterned(func_ty_info.return_type);
  17152                     if (ret_ty.toIntern() == .generic_poison_type) break :opt_val .none;
  17153                     if (ret_ty.zigTypeTag(zcu) == .error_union) {
  17154                         if (ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) {
  17155                             break :opt_val .none;
  17156                         }
  17157                     }
  17158                     break :opt_val ret_ty.toIntern();
  17159                 },
  17160             } });
  17161 
  17162             const callconv_ty = try sema.getBuiltinType(src, .CallingConvention);
  17163             const callconv_val = Value.uninterpret(func_ty_info.cc, callconv_ty, pt) catch |err| switch (err) {
  17164                 error.TypeMismatch => @panic("std.builtin is corrupt"),
  17165                 error.OutOfMemory => |e| return e,
  17166             };
  17167 
  17168             const field_values: [5]InternPool.Index = .{
  17169                 // calling_convention: CallingConvention,
  17170                 callconv_val.toIntern(),
  17171                 // is_generic: bool,
  17172                 Value.makeBool(func_ty_info.is_generic).toIntern(),
  17173                 // is_var_args: bool,
  17174                 Value.makeBool(func_ty_info.is_var_args).toIntern(),
  17175                 // return_type: ?type,
  17176                 ret_ty_opt,
  17177                 // args: []const Fn.Param,
  17178                 args_val,
  17179             };
  17180             return Air.internedToRef((try pt.internUnion(.{
  17181                 .ty = type_info_ty.toIntern(),
  17182                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"fn"))).toIntern(),
  17183                 .val = (try pt.aggregateValue(fn_info_ty, &field_values)).toIntern(),
  17184             })));
  17185         },
  17186         .int => {
  17187             const int_info_ty = try sema.getBuiltinType(src, .@"Type.Int");
  17188             const signedness_ty = try sema.getBuiltinType(src, .Signedness);
  17189             const info = ty.intInfo(zcu);
  17190             const field_values = .{
  17191                 // signedness: Signedness,
  17192                 (try pt.enumValueFieldIndex(signedness_ty, @intFromEnum(info.signedness))).toIntern(),
  17193                 // bits: u16,
  17194                 (try pt.intValue(.u16, info.bits)).toIntern(),
  17195             };
  17196             return Air.internedToRef((try pt.internUnion(.{
  17197                 .ty = type_info_ty.toIntern(),
  17198                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.int))).toIntern(),
  17199                 .val = (try pt.aggregateValue(int_info_ty, &field_values)).toIntern(),
  17200             })));
  17201         },
  17202         .float => {
  17203             const float_info_ty = try sema.getBuiltinType(src, .@"Type.Float");
  17204 
  17205             const field_vals = .{
  17206                 // bits: u16,
  17207                 (try pt.intValue(.u16, ty.bitSize(zcu))).toIntern(),
  17208             };
  17209             return Air.internedToRef((try pt.internUnion(.{
  17210                 .ty = type_info_ty.toIntern(),
  17211                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.float))).toIntern(),
  17212                 .val = (try pt.aggregateValue(float_info_ty, &field_vals)).toIntern(),
  17213             })));
  17214         },
  17215         .pointer => {
  17216             const info = ty.ptrInfo(zcu);
  17217             const alignment = if (info.flags.alignment.toByteUnits()) |alignment|
  17218                 try pt.intValue(.comptime_int, alignment)
  17219             else
  17220                 try Type.fromInterned(info.child).lazyAbiAlignment(pt);
  17221 
  17222             const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace);
  17223             const pointer_ty = try sema.getBuiltinType(src, .@"Type.Pointer");
  17224             const ptr_size_ty = try sema.getBuiltinType(src, .@"Type.Pointer.Size");
  17225 
  17226             const field_values = .{
  17227                 // size: Size,
  17228                 (try pt.enumValueFieldIndex(ptr_size_ty, @intFromEnum(info.flags.size))).toIntern(),
  17229                 // is_const: bool,
  17230                 Value.makeBool(info.flags.is_const).toIntern(),
  17231                 // is_volatile: bool,
  17232                 Value.makeBool(info.flags.is_volatile).toIntern(),
  17233                 // alignment: comptime_int,
  17234                 alignment.toIntern(),
  17235                 // address_space: AddressSpace
  17236                 (try pt.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).toIntern(),
  17237                 // child: type,
  17238                 info.child,
  17239                 // is_allowzero: bool,
  17240                 Value.makeBool(info.flags.is_allowzero).toIntern(),
  17241                 // sentinel: ?*const anyopaque,
  17242                 (try sema.optRefValue(switch (info.sentinel) {
  17243                     .none => null,
  17244                     else => Value.fromInterned(info.sentinel),
  17245                 })).toIntern(),
  17246             };
  17247             return Air.internedToRef((try pt.internUnion(.{
  17248                 .ty = type_info_ty.toIntern(),
  17249                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.pointer))).toIntern(),
  17250                 .val = (try pt.aggregateValue(pointer_ty, &field_values)).toIntern(),
  17251             })));
  17252         },
  17253         .array => {
  17254             const array_field_ty = try sema.getBuiltinType(src, .@"Type.Array");
  17255 
  17256             const info = ty.arrayInfo(zcu);
  17257             const field_values = .{
  17258                 // len: comptime_int,
  17259                 (try pt.intValue(.comptime_int, info.len)).toIntern(),
  17260                 // child: type,
  17261                 info.elem_type.toIntern(),
  17262                 // sentinel: ?*const anyopaque,
  17263                 (try sema.optRefValue(info.sentinel)).toIntern(),
  17264             };
  17265             return Air.internedToRef((try pt.internUnion(.{
  17266                 .ty = type_info_ty.toIntern(),
  17267                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.array))).toIntern(),
  17268                 .val = (try pt.aggregateValue(array_field_ty, &field_values)).toIntern(),
  17269             })));
  17270         },
  17271         .vector => {
  17272             const vector_field_ty = try sema.getBuiltinType(src, .@"Type.Vector");
  17273 
  17274             const info = ty.arrayInfo(zcu);
  17275             const field_values = .{
  17276                 // len: comptime_int,
  17277                 (try pt.intValue(.comptime_int, info.len)).toIntern(),
  17278                 // child: type,
  17279                 info.elem_type.toIntern(),
  17280             };
  17281             return Air.internedToRef((try pt.internUnion(.{
  17282                 .ty = type_info_ty.toIntern(),
  17283                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.vector))).toIntern(),
  17284                 .val = (try pt.aggregateValue(vector_field_ty, &field_values)).toIntern(),
  17285             })));
  17286         },
  17287         .optional => {
  17288             const optional_field_ty = try sema.getBuiltinType(src, .@"Type.Optional");
  17289 
  17290             const field_values = .{
  17291                 // child: type,
  17292                 ty.optionalChild(zcu).toIntern(),
  17293             };
  17294             return Air.internedToRef((try pt.internUnion(.{
  17295                 .ty = type_info_ty.toIntern(),
  17296                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.optional))).toIntern(),
  17297                 .val = (try pt.aggregateValue(optional_field_ty, &field_values)).toIntern(),
  17298             })));
  17299         },
  17300         .error_set => {
  17301             // Get the Error type
  17302             const error_field_ty = try sema.getBuiltinType(src, .@"Type.Error");
  17303 
  17304             // Build our list of Error values
  17305             // Optional value is only null if anyerror
  17306             // Value can be zero-length slice otherwise
  17307             const error_field_vals = switch (try sema.resolveInferredErrorSetTy(block, src, ty.toIntern())) {
  17308                 .anyerror_type => null,
  17309                 else => |err_set_ty_index| blk: {
  17310                     const names = ip.indexToKey(err_set_ty_index).error_set_type.names;
  17311                     const vals = try sema.arena.alloc(InternPool.Index, names.len);
  17312                     for (vals, 0..) |*field_val, error_index| {
  17313                         const error_name = names.get(ip)[error_index];
  17314                         const error_name_len = error_name.length(ip);
  17315                         const error_name_val = v: {
  17316                             const new_decl_ty = try pt.arrayType(.{
  17317                                 .len = error_name_len,
  17318                                 .sentinel = .zero_u8,
  17319                                 .child = .u8_type,
  17320                             });
  17321                             const new_decl_val = try pt.intern(.{ .aggregate = .{
  17322                                 .ty = new_decl_ty.toIntern(),
  17323                                 .storage = .{ .bytes = error_name.toString() },
  17324                             } });
  17325                             break :v try pt.intern(.{ .slice = .{
  17326                                 .ty = .slice_const_u8_sentinel_0_type,
  17327                                 .ptr = try pt.intern(.{ .ptr = .{
  17328                                     .ty = .manyptr_const_u8_sentinel_0_type,
  17329                                     .base_addr = .{ .uav = .{
  17330                                         .val = new_decl_val,
  17331                                         .orig_ty = .slice_const_u8_sentinel_0_type,
  17332                                     } },
  17333                                     .byte_offset = 0,
  17334                                 } }),
  17335                                 .len = (try pt.intValue(.usize, error_name_len)).toIntern(),
  17336                             } });
  17337                         };
  17338 
  17339                         const error_field_fields = .{
  17340                             // name: [:0]const u8,
  17341                             error_name_val,
  17342                         };
  17343                         field_val.* = (try pt.aggregateValue(error_field_ty, &error_field_fields)).toIntern();
  17344                     }
  17345 
  17346                     break :blk vals;
  17347                 },
  17348             };
  17349 
  17350             // Build our ?[]const Error value
  17351             const slice_errors_ty = try pt.ptrTypeSema(.{
  17352                 .child = error_field_ty.toIntern(),
  17353                 .flags = .{
  17354                     .size = .slice,
  17355                     .is_const = true,
  17356                 },
  17357             });
  17358             const opt_slice_errors_ty = try pt.optionalType(slice_errors_ty.toIntern());
  17359             const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: {
  17360                 const array_errors_ty = try pt.arrayType(.{
  17361                     .len = vals.len,
  17362                     .child = error_field_ty.toIntern(),
  17363                 });
  17364                 const new_decl_val = (try pt.aggregateValue(array_errors_ty, vals)).toIntern();
  17365                 const manyptr_errors_ty = slice_errors_ty.slicePtrFieldType(zcu).toIntern();
  17366                 break :v try pt.intern(.{ .slice = .{
  17367                     .ty = slice_errors_ty.toIntern(),
  17368                     .ptr = try pt.intern(.{ .ptr = .{
  17369                         .ty = manyptr_errors_ty,
  17370                         .base_addr = .{ .uav = .{
  17371                             .orig_ty = manyptr_errors_ty,
  17372                             .val = new_decl_val,
  17373                         } },
  17374                         .byte_offset = 0,
  17375                     } }),
  17376                     .len = (try pt.intValue(.usize, vals.len)).toIntern(),
  17377                 } });
  17378             } else .none;
  17379             const errors_val = try pt.intern(.{ .opt = .{
  17380                 .ty = opt_slice_errors_ty.toIntern(),
  17381                 .val = errors_payload_val,
  17382             } });
  17383 
  17384             // Construct Type{ .error_set = errors_val }
  17385             return Air.internedToRef((try pt.internUnion(.{
  17386                 .ty = type_info_ty.toIntern(),
  17387                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.error_set))).toIntern(),
  17388                 .val = errors_val,
  17389             })));
  17390         },
  17391         .error_union => {
  17392             const error_union_field_ty = try sema.getBuiltinType(src, .@"Type.ErrorUnion");
  17393 
  17394             const field_values = .{
  17395                 // error_set: type,
  17396                 ty.errorUnionSet(zcu).toIntern(),
  17397                 // payload: type,
  17398                 ty.errorUnionPayload(zcu).toIntern(),
  17399             };
  17400             return Air.internedToRef((try pt.internUnion(.{
  17401                 .ty = type_info_ty.toIntern(),
  17402                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.error_union))).toIntern(),
  17403                 .val = (try pt.aggregateValue(error_union_field_ty, &field_values)).toIntern(),
  17404             })));
  17405         },
  17406         .@"enum" => {
  17407             const is_exhaustive = Value.makeBool(ip.loadEnumType(ty.toIntern()).tag_mode != .nonexhaustive);
  17408 
  17409             const enum_field_ty = try sema.getBuiltinType(src, .@"Type.EnumField");
  17410 
  17411             const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.loadEnumType(ty.toIntern()).names.len);
  17412             for (enum_field_vals, 0..) |*field_val, tag_index| {
  17413                 const enum_type = ip.loadEnumType(ty.toIntern());
  17414                 const value_val = if (enum_type.values.len > 0)
  17415                     try ip.getCoercedInts(
  17416                         zcu.gpa,
  17417                         pt.tid,
  17418                         ip.indexToKey(enum_type.values.get(ip)[tag_index]).int,
  17419                         .comptime_int_type,
  17420                     )
  17421                 else
  17422                     (try pt.intValue(.comptime_int, tag_index)).toIntern();
  17423 
  17424                 // TODO: write something like getCoercedInts to avoid needing to dupe
  17425                 const name_val = v: {
  17426                     const tag_name = enum_type.names.get(ip)[tag_index];
  17427                     const tag_name_len = tag_name.length(ip);
  17428                     const new_decl_ty = try pt.arrayType(.{
  17429                         .len = tag_name_len,
  17430                         .sentinel = .zero_u8,
  17431                         .child = .u8_type,
  17432                     });
  17433                     const new_decl_val = try pt.intern(.{ .aggregate = .{
  17434                         .ty = new_decl_ty.toIntern(),
  17435                         .storage = .{ .bytes = tag_name.toString() },
  17436                     } });
  17437                     break :v try pt.intern(.{ .slice = .{
  17438                         .ty = .slice_const_u8_sentinel_0_type,
  17439                         .ptr = try pt.intern(.{ .ptr = .{
  17440                             .ty = .manyptr_const_u8_sentinel_0_type,
  17441                             .base_addr = .{ .uav = .{
  17442                                 .val = new_decl_val,
  17443                                 .orig_ty = .slice_const_u8_sentinel_0_type,
  17444                             } },
  17445                             .byte_offset = 0,
  17446                         } }),
  17447                         .len = (try pt.intValue(.usize, tag_name_len)).toIntern(),
  17448                     } });
  17449                 };
  17450 
  17451                 const enum_field_fields = .{
  17452                     // name: [:0]const u8,
  17453                     name_val,
  17454                     // value: comptime_int,
  17455                     value_val,
  17456                 };
  17457                 field_val.* = (try pt.aggregateValue(enum_field_ty, &enum_field_fields)).toIntern();
  17458             }
  17459 
  17460             const fields_val = v: {
  17461                 const fields_array_ty = try pt.arrayType(.{
  17462                     .len = enum_field_vals.len,
  17463                     .child = enum_field_ty.toIntern(),
  17464                 });
  17465                 const new_decl_val = (try pt.aggregateValue(fields_array_ty, enum_field_vals)).toIntern();
  17466                 const slice_ty = (try pt.ptrTypeSema(.{
  17467                     .child = enum_field_ty.toIntern(),
  17468                     .flags = .{
  17469                         .size = .slice,
  17470                         .is_const = true,
  17471                     },
  17472                 })).toIntern();
  17473                 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  17474                 break :v try pt.intern(.{ .slice = .{
  17475                     .ty = slice_ty,
  17476                     .ptr = try pt.intern(.{ .ptr = .{
  17477                         .ty = manyptr_ty,
  17478                         .base_addr = .{ .uav = .{
  17479                             .val = new_decl_val,
  17480                             .orig_ty = manyptr_ty,
  17481                         } },
  17482                         .byte_offset = 0,
  17483                     } }),
  17484                     .len = (try pt.intValue(.usize, enum_field_vals.len)).toIntern(),
  17485                 } });
  17486             };
  17487 
  17488             const decls_val = try sema.typeInfoDecls(src, ip.loadEnumType(ty.toIntern()).namespace.toOptional());
  17489 
  17490             const type_enum_ty = try sema.getBuiltinType(src, .@"Type.Enum");
  17491 
  17492             const field_values = .{
  17493                 // tag_type: type,
  17494                 ip.loadEnumType(ty.toIntern()).tag_ty,
  17495                 // fields: []const EnumField,
  17496                 fields_val,
  17497                 // decls: []const Declaration,
  17498                 decls_val,
  17499                 // is_exhaustive: bool,
  17500                 is_exhaustive.toIntern(),
  17501             };
  17502             return Air.internedToRef((try pt.internUnion(.{
  17503                 .ty = type_info_ty.toIntern(),
  17504                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"enum"))).toIntern(),
  17505                 .val = (try pt.aggregateValue(type_enum_ty, &field_values)).toIntern(),
  17506             })));
  17507         },
  17508         .@"union" => {
  17509             const type_union_ty = try sema.getBuiltinType(src, .@"Type.Union");
  17510             const union_field_ty = try sema.getBuiltinType(src, .@"Type.UnionField");
  17511 
  17512             try ty.resolveLayout(pt); // Getting alignment requires type layout
  17513             const union_obj = zcu.typeToUnion(ty).?;
  17514             const tag_type = union_obj.loadTagType(ip);
  17515             const layout = union_obj.flagsUnordered(ip).layout;
  17516 
  17517             const union_field_vals = try gpa.alloc(InternPool.Index, tag_type.names.len);
  17518             defer gpa.free(union_field_vals);
  17519 
  17520             for (union_field_vals, 0..) |*field_val, field_index| {
  17521                 const name_val = v: {
  17522                     const field_name = tag_type.names.get(ip)[field_index];
  17523                     const field_name_len = field_name.length(ip);
  17524                     const new_decl_ty = try pt.arrayType(.{
  17525                         .len = field_name_len,
  17526                         .sentinel = .zero_u8,
  17527                         .child = .u8_type,
  17528                     });
  17529                     const new_decl_val = try pt.intern(.{ .aggregate = .{
  17530                         .ty = new_decl_ty.toIntern(),
  17531                         .storage = .{ .bytes = field_name.toString() },
  17532                     } });
  17533                     break :v try pt.intern(.{ .slice = .{
  17534                         .ty = .slice_const_u8_sentinel_0_type,
  17535                         .ptr = try pt.intern(.{ .ptr = .{
  17536                             .ty = .manyptr_const_u8_sentinel_0_type,
  17537                             .base_addr = .{ .uav = .{
  17538                                 .val = new_decl_val,
  17539                                 .orig_ty = .slice_const_u8_sentinel_0_type,
  17540                             } },
  17541                             .byte_offset = 0,
  17542                         } }),
  17543                         .len = (try pt.intValue(.usize, field_name_len)).toIntern(),
  17544                     } });
  17545                 };
  17546 
  17547                 const alignment = switch (layout) {
  17548                     .auto, .@"extern" => try ty.fieldAlignmentSema(field_index, pt),
  17549                     .@"packed" => .none,
  17550                 };
  17551 
  17552                 const field_ty = union_obj.field_types.get(ip)[field_index];
  17553                 const union_field_fields = .{
  17554                     // name: [:0]const u8,
  17555                     name_val,
  17556                     // type: type,
  17557                     field_ty,
  17558                     // alignment: comptime_int,
  17559                     (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(),
  17560                 };
  17561                 field_val.* = (try pt.aggregateValue(union_field_ty, &union_field_fields)).toIntern();
  17562             }
  17563 
  17564             const fields_val = v: {
  17565                 const array_fields_ty = try pt.arrayType(.{
  17566                     .len = union_field_vals.len,
  17567                     .child = union_field_ty.toIntern(),
  17568                 });
  17569                 const new_decl_val = (try pt.aggregateValue(array_fields_ty, union_field_vals)).toIntern();
  17570                 const slice_ty = (try pt.ptrTypeSema(.{
  17571                     .child = union_field_ty.toIntern(),
  17572                     .flags = .{
  17573                         .size = .slice,
  17574                         .is_const = true,
  17575                     },
  17576                 })).toIntern();
  17577                 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  17578                 break :v try pt.intern(.{ .slice = .{
  17579                     .ty = slice_ty,
  17580                     .ptr = try pt.intern(.{ .ptr = .{
  17581                         .ty = manyptr_ty,
  17582                         .base_addr = .{ .uav = .{
  17583                             .orig_ty = manyptr_ty,
  17584                             .val = new_decl_val,
  17585                         } },
  17586                         .byte_offset = 0,
  17587                     } }),
  17588                     .len = (try pt.intValue(.usize, union_field_vals.len)).toIntern(),
  17589                 } });
  17590             };
  17591 
  17592             const decls_val = try sema.typeInfoDecls(src, ty.getNamespaceIndex(zcu).toOptional());
  17593 
  17594             const enum_tag_ty_val = try pt.intern(.{ .opt = .{
  17595                 .ty = (try pt.optionalType(.type_type)).toIntern(),
  17596                 .val = if (ty.unionTagType(zcu)) |tag_ty| tag_ty.toIntern() else .none,
  17597             } });
  17598 
  17599             const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout");
  17600 
  17601             const field_values = .{
  17602                 // layout: ContainerLayout,
  17603                 (try pt.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(),
  17604 
  17605                 // tag_type: ?type,
  17606                 enum_tag_ty_val,
  17607                 // fields: []const UnionField,
  17608                 fields_val,
  17609                 // decls: []const Declaration,
  17610                 decls_val,
  17611             };
  17612             return Air.internedToRef((try pt.internUnion(.{
  17613                 .ty = type_info_ty.toIntern(),
  17614                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"union"))).toIntern(),
  17615                 .val = (try pt.aggregateValue(type_union_ty, &field_values)).toIntern(),
  17616             })));
  17617         },
  17618         .@"struct" => {
  17619             const type_struct_ty = try sema.getBuiltinType(src, .@"Type.Struct");
  17620             const struct_field_ty = try sema.getBuiltinType(src, .@"Type.StructField");
  17621 
  17622             try ty.resolveLayout(pt); // Getting alignment requires type layout
  17623 
  17624             var struct_field_vals: []InternPool.Index = &.{};
  17625             defer gpa.free(struct_field_vals);
  17626             fv: {
  17627                 const struct_type = switch (ip.indexToKey(ty.toIntern())) {
  17628                     .tuple_type => |tuple_type| {
  17629                         struct_field_vals = try gpa.alloc(InternPool.Index, tuple_type.types.len);
  17630                         for (struct_field_vals, 0..) |*struct_field_val, field_index| {
  17631                             const field_ty = tuple_type.types.get(ip)[field_index];
  17632                             const field_val = tuple_type.values.get(ip)[field_index];
  17633                             const name_val = v: {
  17634                                 const field_name = try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
  17635                                 const field_name_len = field_name.length(ip);
  17636                                 const new_decl_ty = try pt.arrayType(.{
  17637                                     .len = field_name_len,
  17638                                     .sentinel = .zero_u8,
  17639                                     .child = .u8_type,
  17640                                 });
  17641                                 const new_decl_val = try pt.intern(.{ .aggregate = .{
  17642                                     .ty = new_decl_ty.toIntern(),
  17643                                     .storage = .{ .bytes = field_name.toString() },
  17644                                 } });
  17645                                 break :v try pt.intern(.{ .slice = .{
  17646                                     .ty = .slice_const_u8_sentinel_0_type,
  17647                                     .ptr = try pt.intern(.{ .ptr = .{
  17648                                         .ty = .manyptr_const_u8_sentinel_0_type,
  17649                                         .base_addr = .{ .uav = .{
  17650                                             .val = new_decl_val,
  17651                                             .orig_ty = .slice_const_u8_sentinel_0_type,
  17652                                         } },
  17653                                         .byte_offset = 0,
  17654                                     } }),
  17655                                     .len = (try pt.intValue(.usize, field_name_len)).toIntern(),
  17656                                 } });
  17657                             };
  17658 
  17659                             try Type.fromInterned(field_ty).resolveLayout(pt);
  17660 
  17661                             const is_comptime = field_val != .none;
  17662                             const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null;
  17663                             const default_val_ptr = try sema.optRefValue(opt_default_val);
  17664                             const struct_field_fields = .{
  17665                                 // name: [:0]const u8,
  17666                                 name_val,
  17667                                 // type: type,
  17668                                 field_ty,
  17669                                 // default_value: ?*const anyopaque,
  17670                                 default_val_ptr.toIntern(),
  17671                                 // is_comptime: bool,
  17672                                 Value.makeBool(is_comptime).toIntern(),
  17673                                 // alignment: comptime_int,
  17674                                 (try pt.intValue(.comptime_int, Type.fromInterned(field_ty).abiAlignment(zcu).toByteUnits() orelse 0)).toIntern(),
  17675                             };
  17676                             struct_field_val.* = (try pt.aggregateValue(struct_field_ty, &struct_field_fields)).toIntern();
  17677                         }
  17678                         break :fv;
  17679                     },
  17680                     .struct_type => ip.loadStructType(ty.toIntern()),
  17681                     else => unreachable,
  17682                 };
  17683                 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
  17684 
  17685                 try ty.resolveStructFieldInits(pt);
  17686 
  17687                 for (struct_field_vals, 0..) |*field_val, field_index| {
  17688                     const field_name = if (struct_type.fieldName(ip, field_index).unwrap()) |field_name|
  17689                         field_name
  17690                     else
  17691                         try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
  17692                     const field_name_len = field_name.length(ip);
  17693                     const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
  17694                     const field_init = struct_type.fieldInit(ip, field_index);
  17695                     const field_is_comptime = struct_type.fieldIsComptime(ip, field_index);
  17696                     const name_val = v: {
  17697                         const new_decl_ty = try pt.arrayType(.{
  17698                             .len = field_name_len,
  17699                             .sentinel = .zero_u8,
  17700                             .child = .u8_type,
  17701                         });
  17702                         const new_decl_val = try pt.intern(.{ .aggregate = .{
  17703                             .ty = new_decl_ty.toIntern(),
  17704                             .storage = .{ .bytes = field_name.toString() },
  17705                         } });
  17706                         break :v try pt.intern(.{ .slice = .{
  17707                             .ty = .slice_const_u8_sentinel_0_type,
  17708                             .ptr = try pt.intern(.{ .ptr = .{
  17709                                 .ty = .manyptr_const_u8_sentinel_0_type,
  17710                                 .base_addr = .{ .uav = .{
  17711                                     .val = new_decl_val,
  17712                                     .orig_ty = .slice_const_u8_sentinel_0_type,
  17713                                 } },
  17714                                 .byte_offset = 0,
  17715                             } }),
  17716                             .len = (try pt.intValue(.usize, field_name_len)).toIntern(),
  17717                         } });
  17718                     };
  17719 
  17720                     const opt_default_val = if (field_init == .none) null else Value.fromInterned(field_init);
  17721                     const default_val_ptr = try sema.optRefValue(opt_default_val);
  17722                     const alignment = switch (struct_type.layout) {
  17723                         .@"packed" => .none,
  17724                         else => try field_ty.structFieldAlignmentSema(
  17725                             struct_type.fieldAlign(ip, field_index),
  17726                             struct_type.layout,
  17727                             pt,
  17728                         ),
  17729                     };
  17730 
  17731                     const struct_field_fields = .{
  17732                         // name: [:0]const u8,
  17733                         name_val,
  17734                         // type: type,
  17735                         field_ty.toIntern(),
  17736                         // default_value: ?*const anyopaque,
  17737                         default_val_ptr.toIntern(),
  17738                         // is_comptime: bool,
  17739                         Value.makeBool(field_is_comptime).toIntern(),
  17740                         // alignment: comptime_int,
  17741                         (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(),
  17742                     };
  17743                     field_val.* = (try pt.aggregateValue(struct_field_ty, &struct_field_fields)).toIntern();
  17744                 }
  17745             }
  17746 
  17747             const fields_val = v: {
  17748                 const array_fields_ty = try pt.arrayType(.{
  17749                     .len = struct_field_vals.len,
  17750                     .child = struct_field_ty.toIntern(),
  17751                 });
  17752                 const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_vals)).toIntern();
  17753                 const slice_ty = (try pt.ptrTypeSema(.{
  17754                     .child = struct_field_ty.toIntern(),
  17755                     .flags = .{
  17756                         .size = .slice,
  17757                         .is_const = true,
  17758                     },
  17759                 })).toIntern();
  17760                 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  17761                 break :v try pt.intern(.{ .slice = .{
  17762                     .ty = slice_ty,
  17763                     .ptr = try pt.intern(.{ .ptr = .{
  17764                         .ty = manyptr_ty,
  17765                         .base_addr = .{ .uav = .{
  17766                             .orig_ty = manyptr_ty,
  17767                             .val = new_decl_val,
  17768                         } },
  17769                         .byte_offset = 0,
  17770                     } }),
  17771                     .len = (try pt.intValue(.usize, struct_field_vals.len)).toIntern(),
  17772                 } });
  17773             };
  17774 
  17775             const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu));
  17776 
  17777             const backing_integer_val = try pt.intern(.{ .opt = .{
  17778                 .ty = (try pt.optionalType(.type_type)).toIntern(),
  17779                 .val = if (zcu.typeToPackedStruct(ty)) |packed_struct| val: {
  17780                     assert(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)).isInt(zcu));
  17781                     break :val packed_struct.backingIntTypeUnordered(ip);
  17782                 } else .none,
  17783             } });
  17784 
  17785             const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout");
  17786 
  17787             const layout = ty.containerLayout(zcu);
  17788 
  17789             const field_values = [_]InternPool.Index{
  17790                 // layout: ContainerLayout,
  17791                 (try pt.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(),
  17792                 // backing_integer: ?type,
  17793                 backing_integer_val,
  17794                 // fields: []const StructField,
  17795                 fields_val,
  17796                 // decls: []const Declaration,
  17797                 decls_val,
  17798                 // is_tuple: bool,
  17799                 Value.makeBool(ty.isTuple(zcu)).toIntern(),
  17800             };
  17801             return Air.internedToRef((try pt.internUnion(.{
  17802                 .ty = type_info_ty.toIntern(),
  17803                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"struct"))).toIntern(),
  17804                 .val = (try pt.aggregateValue(type_struct_ty, &field_values)).toIntern(),
  17805             })));
  17806         },
  17807         .@"opaque" => {
  17808             const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque");
  17809 
  17810             try ty.resolveFields(pt);
  17811             const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu));
  17812 
  17813             const field_values = .{
  17814                 // decls: []const Declaration,
  17815                 decls_val,
  17816             };
  17817             return Air.internedToRef((try pt.internUnion(.{
  17818                 .ty = type_info_ty.toIntern(),
  17819                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"opaque"))).toIntern(),
  17820                 .val = (try pt.aggregateValue(type_opaque_ty, &field_values)).toIntern(),
  17821             })));
  17822         },
  17823         .frame => return sema.failWithUseOfAsync(block, src),
  17824         .@"anyframe" => return sema.failWithUseOfAsync(block, src),
  17825     }
  17826 }
  17827 
  17828 fn typeInfoDecls(
  17829     sema: *Sema,
  17830     src: LazySrcLoc,
  17831     opt_namespace: InternPool.OptionalNamespaceIndex,
  17832 ) CompileError!InternPool.Index {
  17833     const pt = sema.pt;
  17834     const zcu = pt.zcu;
  17835     const gpa = sema.gpa;
  17836 
  17837     const declaration_ty = try sema.getBuiltinType(src, .@"Type.Declaration");
  17838 
  17839     var decl_vals = std.array_list.Managed(InternPool.Index).init(gpa);
  17840     defer decl_vals.deinit();
  17841 
  17842     var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa);
  17843     defer seen_namespaces.deinit();
  17844 
  17845     try sema.typeInfoNamespaceDecls(opt_namespace, declaration_ty, &decl_vals, &seen_namespaces);
  17846 
  17847     const array_decl_ty = try pt.arrayType(.{
  17848         .len = decl_vals.items.len,
  17849         .child = declaration_ty.toIntern(),
  17850     });
  17851     const new_decl_val = (try pt.aggregateValue(array_decl_ty, decl_vals.items)).toIntern();
  17852     const slice_ty = (try pt.ptrTypeSema(.{
  17853         .child = declaration_ty.toIntern(),
  17854         .flags = .{
  17855             .size = .slice,
  17856             .is_const = true,
  17857         },
  17858     })).toIntern();
  17859     const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  17860     return try pt.intern(.{ .slice = .{
  17861         .ty = slice_ty,
  17862         .ptr = try pt.intern(.{ .ptr = .{
  17863             .ty = manyptr_ty,
  17864             .base_addr = .{ .uav = .{
  17865                 .orig_ty = manyptr_ty,
  17866                 .val = new_decl_val,
  17867             } },
  17868             .byte_offset = 0,
  17869         } }),
  17870         .len = (try pt.intValue(.usize, decl_vals.items.len)).toIntern(),
  17871     } });
  17872 }
  17873 
  17874 fn typeInfoNamespaceDecls(
  17875     sema: *Sema,
  17876     opt_namespace_index: InternPool.OptionalNamespaceIndex,
  17877     declaration_ty: Type,
  17878     decl_vals: *std.array_list.Managed(InternPool.Index),
  17879     seen_namespaces: *std.AutoHashMap(*Namespace, void),
  17880 ) !void {
  17881     const pt = sema.pt;
  17882     const zcu = pt.zcu;
  17883     const ip = &zcu.intern_pool;
  17884 
  17885     const namespace_index = opt_namespace_index.unwrap() orelse return;
  17886     try pt.ensureNamespaceUpToDate(namespace_index);
  17887     const namespace = zcu.namespacePtr(namespace_index);
  17888 
  17889     const gop = try seen_namespaces.getOrPut(namespace);
  17890     if (gop.found_existing) return;
  17891 
  17892     for (namespace.pub_decls.keys()) |nav| {
  17893         const name = ip.getNav(nav).name;
  17894         const name_val = name_val: {
  17895             const name_len = name.length(ip);
  17896             const array_ty = try pt.arrayType(.{
  17897                 .len = name_len,
  17898                 .sentinel = .zero_u8,
  17899                 .child = .u8_type,
  17900             });
  17901             const array_val = try pt.intern(.{ .aggregate = .{
  17902                 .ty = array_ty.toIntern(),
  17903                 .storage = .{ .bytes = name.toString() },
  17904             } });
  17905             break :name_val try pt.intern(.{
  17906                 .slice = .{
  17907                     .ty = .slice_const_u8_sentinel_0_type, // [:0]const u8
  17908                     .ptr = try pt.intern(.{
  17909                         .ptr = .{
  17910                             .ty = .manyptr_const_u8_sentinel_0_type, // [*:0]const u8
  17911                             .base_addr = .{ .uav = .{
  17912                                 .orig_ty = .slice_const_u8_sentinel_0_type,
  17913                                 .val = array_val,
  17914                             } },
  17915                             .byte_offset = 0,
  17916                         },
  17917                     }),
  17918                     .len = (try pt.intValue(.usize, name_len)).toIntern(),
  17919                 },
  17920             });
  17921         };
  17922         const fields = [_]InternPool.Index{
  17923             // name: [:0]const u8,
  17924             name_val,
  17925         };
  17926         try decl_vals.append((try pt.aggregateValue(declaration_ty, &fields)).toIntern());
  17927     }
  17928 }
  17929 
  17930 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17931     _ = block;
  17932     const zir_datas = sema.code.instructions.items(.data);
  17933     const inst_data = zir_datas[@intFromEnum(inst)].un_node;
  17934     const operand = try sema.resolveInst(inst_data.operand);
  17935     const operand_ty = sema.typeOf(operand);
  17936     return Air.internedToRef(operand_ty.toIntern());
  17937 }
  17938 
  17939 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17940     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  17941     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
  17942     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  17943 
  17944     var child_block: Block = .{
  17945         .parent = block,
  17946         .sema = sema,
  17947         .namespace = block.namespace,
  17948         .instructions = .{},
  17949         .inlining = block.inlining,
  17950         .comptime_reason = null,
  17951         .is_typeof = true,
  17952         .want_safety = false,
  17953         .error_return_trace_index = block.error_return_trace_index,
  17954         .src_base_inst = block.src_base_inst,
  17955         .type_name_ctx = block.type_name_ctx,
  17956     };
  17957     defer child_block.instructions.deinit(sema.gpa);
  17958 
  17959     const operand = try sema.resolveInlineBody(&child_block, body, inst);
  17960     return Air.internedToRef(sema.typeOf(operand).toIntern());
  17961 }
  17962 
  17963 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17964     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  17965     const src = block.nodeOffset(inst_data.src_node);
  17966     const operand = try sema.resolveInst(inst_data.operand);
  17967     const operand_ty = sema.typeOf(operand);
  17968     const res_ty = try sema.log2IntType(block, operand_ty, src);
  17969     return Air.internedToRef(res_ty.toIntern());
  17970 }
  17971 
  17972 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type {
  17973     const pt = sema.pt;
  17974     const zcu = pt.zcu;
  17975     switch (operand.zigTypeTag(zcu)) {
  17976         .comptime_int => return .comptime_int,
  17977         .int => {
  17978             const bits = operand.bitSize(zcu);
  17979             const count = if (bits == 0)
  17980                 0
  17981             else blk: {
  17982                 var count: u16 = 0;
  17983                 var s = bits - 1;
  17984                 while (s != 0) : (s >>= 1) {
  17985                     count += 1;
  17986                 }
  17987                 break :blk count;
  17988             };
  17989             return pt.intType(.unsigned, count);
  17990         },
  17991         .vector => {
  17992             const elem_ty = operand.elemType2(zcu);
  17993             const log2_elem_ty = try sema.log2IntType(block, elem_ty, src);
  17994             return pt.vectorType(.{
  17995                 .len = operand.vectorLen(zcu),
  17996                 .child = log2_elem_ty.toIntern(),
  17997             });
  17998         },
  17999         else => {},
  18000     }
  18001     return sema.fail(
  18002         block,
  18003         src,
  18004         "bit shifting operation expected integer type, found '{f}'",
  18005         .{operand.fmt(pt)},
  18006     );
  18007 }
  18008 
  18009 fn zirTypeofPeer(
  18010     sema: *Sema,
  18011     block: *Block,
  18012     extended: Zir.Inst.Extended.InstData,
  18013     inst: Zir.Inst.Index,
  18014 ) CompileError!Air.Inst.Ref {
  18015     const tracy = trace(@src());
  18016     defer tracy.end();
  18017 
  18018     const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
  18019     const src = block.nodeOffset(extra.data.src_node);
  18020     const body = sema.code.bodySlice(extra.data.body_index, extra.data.body_len);
  18021 
  18022     var child_block: Block = .{
  18023         .parent = block,
  18024         .sema = sema,
  18025         .namespace = block.namespace,
  18026         .instructions = .{},
  18027         .inlining = block.inlining,
  18028         .comptime_reason = null,
  18029         .is_typeof = true,
  18030         .runtime_cond = block.runtime_cond,
  18031         .runtime_loop = block.runtime_loop,
  18032         .runtime_index = block.runtime_index,
  18033         .src_base_inst = block.src_base_inst,
  18034         .type_name_ctx = block.type_name_ctx,
  18035     };
  18036     defer child_block.instructions.deinit(sema.gpa);
  18037     // Ignore the result, we only care about the instructions in `args`.
  18038     _ = try sema.analyzeInlineBody(&child_block, body, inst);
  18039 
  18040     const args = sema.code.refSlice(extra.end, extended.small);
  18041 
  18042     const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len);
  18043     defer sema.gpa.free(inst_list);
  18044 
  18045     for (args, 0..) |arg_ref, i| {
  18046         inst_list[i] = try sema.resolveInst(arg_ref);
  18047     }
  18048 
  18049     const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node });
  18050     return Air.internedToRef(result_type.toIntern());
  18051 }
  18052 
  18053 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18054     const pt = sema.pt;
  18055     const zcu = pt.zcu;
  18056     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18057     const src = block.nodeOffset(inst_data.src_node);
  18058     const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
  18059     const uncasted_operand = try sema.resolveInst(inst_data.operand);
  18060     const uncasted_ty = sema.typeOf(uncasted_operand);
  18061     if (uncasted_ty.isVector(zcu)) {
  18062         if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) {
  18063             return sema.fail(block, operand_src, "boolean not operation on type '{f}'", .{
  18064                 uncasted_ty.fmt(pt),
  18065             });
  18066         }
  18067         return analyzeBitNot(sema, block, uncasted_operand, src);
  18068     }
  18069     const operand = try sema.coerce(block, .bool, uncasted_operand, operand_src);
  18070     if (try sema.resolveValue(operand)) |val| {
  18071         return if (val.isUndef(zcu)) .undef_bool else if (val.toBool()) .bool_false else .bool_true;
  18072     }
  18073     try sema.requireRuntimeBlock(block, src, null);
  18074     return block.addTyOp(.not, .bool, operand);
  18075 }
  18076 
  18077 fn zirBoolBr(
  18078     sema: *Sema,
  18079     parent_block: *Block,
  18080     inst: Zir.Inst.Index,
  18081     is_bool_or: bool,
  18082 ) CompileError!Air.Inst.Ref {
  18083     const tracy = trace(@src());
  18084     defer tracy.end();
  18085 
  18086     const pt = sema.pt;
  18087     const zcu = pt.zcu;
  18088     const gpa = sema.gpa;
  18089 
  18090     const datas = sema.code.instructions.items(.data);
  18091     const inst_data = datas[@intFromEnum(inst)].pl_node;
  18092     const extra = sema.code.extraData(Zir.Inst.BoolBr, inst_data.payload_index);
  18093 
  18094     const uncoerced_lhs = try sema.resolveInst(extra.data.lhs);
  18095     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  18096     const lhs_src = parent_block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  18097     const rhs_src = parent_block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  18098 
  18099     const lhs = try sema.coerce(parent_block, .bool, uncoerced_lhs, lhs_src);
  18100 
  18101     if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
  18102         if (is_bool_or and lhs_val.toBool()) {
  18103             return .bool_true;
  18104         } else if (!is_bool_or and !lhs_val.toBool()) {
  18105             return .bool_false;
  18106         }
  18107         // comptime-known left-hand side. No need for a block here; the result
  18108         // is simply the rhs expression. Here we rely on there only being 1
  18109         // break instruction (`break_inline`).
  18110         const rhs_result = try sema.resolveInlineBody(parent_block, body, inst);
  18111         if (sema.typeOf(rhs_result).isNoReturn(zcu)) {
  18112             return rhs_result;
  18113         }
  18114         return sema.coerce(parent_block, .bool, rhs_result, rhs_src);
  18115     }
  18116 
  18117     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  18118     try sema.air_instructions.append(gpa, .{
  18119         .tag = .block,
  18120         .data = .{ .ty_pl = .{
  18121             .ty = .bool_type,
  18122             .payload = undefined,
  18123         } },
  18124     });
  18125 
  18126     var child_block = parent_block.makeSubBlock();
  18127     child_block.runtime_loop = null;
  18128     child_block.runtime_cond = lhs_src;
  18129     child_block.runtime_index.increment();
  18130     defer child_block.instructions.deinit(gpa);
  18131 
  18132     var then_block = child_block.makeSubBlock();
  18133     defer then_block.instructions.deinit(gpa);
  18134 
  18135     var else_block = child_block.makeSubBlock();
  18136     defer else_block.instructions.deinit(gpa);
  18137 
  18138     const lhs_block = if (is_bool_or) &then_block else &else_block;
  18139     const rhs_block = if (is_bool_or) &else_block else &then_block;
  18140 
  18141     const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
  18142     _ = try lhs_block.addBr(block_inst, lhs_result);
  18143 
  18144     const parent_hint = sema.branch_hint;
  18145     defer sema.branch_hint = parent_hint;
  18146     sema.branch_hint = null;
  18147 
  18148     const rhs_result = try sema.resolveInlineBody(rhs_block, body, inst);
  18149     const rhs_noret = sema.typeOf(rhs_result).isNoReturn(zcu);
  18150     const coerced_rhs_result = if (!rhs_noret) rhs: {
  18151         const coerced_result = try sema.coerce(rhs_block, .bool, rhs_result, rhs_src);
  18152         _ = try rhs_block.addBr(block_inst, coerced_result);
  18153         break :rhs coerced_result;
  18154     } else rhs_result;
  18155 
  18156     const rhs_hint = sema.branch_hint orelse .none;
  18157 
  18158     const result = try sema.finishCondBr(
  18159         parent_block,
  18160         &child_block,
  18161         &then_block,
  18162         &else_block,
  18163         lhs,
  18164         block_inst,
  18165         if (is_bool_or) .{
  18166             .true = .none,
  18167             .false = rhs_hint,
  18168             .then_cov = .poi,
  18169             .else_cov = .poi,
  18170         } else .{
  18171             .true = rhs_hint,
  18172             .false = .none,
  18173             .then_cov = .poi,
  18174             .else_cov = .poi,
  18175         },
  18176     );
  18177     if (!rhs_noret) {
  18178         if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| {
  18179             if (is_bool_or and rhs_val.toBool()) {
  18180                 return .bool_true;
  18181             } else if (!is_bool_or and !rhs_val.toBool()) {
  18182                 return .bool_false;
  18183             }
  18184         }
  18185     }
  18186 
  18187     return result;
  18188 }
  18189 
  18190 fn finishCondBr(
  18191     sema: *Sema,
  18192     parent_block: *Block,
  18193     child_block: *Block,
  18194     then_block: *Block,
  18195     else_block: *Block,
  18196     cond: Air.Inst.Ref,
  18197     block_inst: Air.Inst.Index,
  18198     branch_hints: Air.CondBr.BranchHints,
  18199 ) !Air.Inst.Ref {
  18200     const gpa = sema.gpa;
  18201 
  18202     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
  18203         then_block.instructions.items.len + else_block.instructions.items.len +
  18204         @typeInfo(Air.Block).@"struct".fields.len + child_block.instructions.items.len + 1);
  18205 
  18206     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18207         .then_body_len = @intCast(then_block.instructions.items.len),
  18208         .else_body_len = @intCast(else_block.instructions.items.len),
  18209         .branch_hints = branch_hints,
  18210     });
  18211     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
  18212     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
  18213 
  18214     _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  18215         .operand = cond,
  18216         .payload = cond_br_payload,
  18217     } } });
  18218 
  18219     sema.air_instructions.items(.data)[@intFromEnum(block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(
  18220         Air.Block{ .body_len = @intCast(child_block.instructions.items.len) },
  18221     );
  18222     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
  18223 
  18224     try parent_block.instructions.append(gpa, block_inst);
  18225     return block_inst.toRef();
  18226 }
  18227 
  18228 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  18229     const pt = sema.pt;
  18230     const zcu = pt.zcu;
  18231     switch (ty.zigTypeTag(zcu)) {
  18232         .optional, .null, .undefined => return,
  18233         .pointer => if (ty.isPtrLikeOptional(zcu)) return,
  18234         else => {},
  18235     }
  18236     return sema.failWithExpectedOptionalType(block, src, ty);
  18237 }
  18238 
  18239 fn checkSentinelType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  18240     const pt = sema.pt;
  18241     const zcu = pt.zcu;
  18242     if (!ty.isSelfComparable(zcu, true)) {
  18243         return sema.fail(block, src, "non-scalar sentinel type '{f}'", .{ty.fmt(pt)});
  18244     }
  18245 }
  18246 
  18247 fn zirIsNonNull(
  18248     sema: *Sema,
  18249     block: *Block,
  18250     inst: Zir.Inst.Index,
  18251 ) CompileError!Air.Inst.Ref {
  18252     const tracy = trace(@src());
  18253     defer tracy.end();
  18254 
  18255     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18256     const src = block.nodeOffset(inst_data.src_node);
  18257     const operand = try sema.resolveInst(inst_data.operand);
  18258     try sema.checkNullableType(block, src, sema.typeOf(operand));
  18259     return sema.analyzeIsNull(block, operand, true);
  18260 }
  18261 
  18262 fn zirIsNonNullPtr(
  18263     sema: *Sema,
  18264     block: *Block,
  18265     inst: Zir.Inst.Index,
  18266 ) CompileError!Air.Inst.Ref {
  18267     const tracy = trace(@src());
  18268     defer tracy.end();
  18269 
  18270     const pt = sema.pt;
  18271     const zcu = pt.zcu;
  18272     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18273     const src = block.nodeOffset(inst_data.src_node);
  18274     const ptr = try sema.resolveInst(inst_data.operand);
  18275     const ptr_ty = sema.typeOf(ptr);
  18276     try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(zcu));
  18277     if (try sema.resolveValue(ptr)) |ptr_val| {
  18278         if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |loaded_val| {
  18279             return sema.analyzeIsNull(block, Air.internedToRef(loaded_val.toIntern()), true);
  18280         }
  18281     }
  18282     if (ptr_ty.childType(zcu).isNullFromType(zcu)) |is_null| {
  18283         return if (is_null) .bool_false else .bool_true;
  18284     }
  18285     return block.addUnOp(.is_non_null_ptr, ptr);
  18286 }
  18287 
  18288 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  18289     const pt = sema.pt;
  18290     const zcu = pt.zcu;
  18291     switch (ty.zigTypeTag(zcu)) {
  18292         .error_set, .error_union, .undefined => return,
  18293         else => return sema.fail(block, src, "expected error union type, found '{f}'", .{
  18294             ty.fmt(pt),
  18295         }),
  18296     }
  18297 }
  18298 
  18299 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18300     const tracy = trace(@src());
  18301     defer tracy.end();
  18302 
  18303     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18304     const src = block.nodeOffset(inst_data.src_node);
  18305     const operand = try sema.resolveInst(inst_data.operand);
  18306     try sema.checkErrorType(block, src, sema.typeOf(operand));
  18307     return sema.analyzeIsNonErr(block, src, operand);
  18308 }
  18309 
  18310 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18311     const tracy = trace(@src());
  18312     defer tracy.end();
  18313 
  18314     const pt = sema.pt;
  18315     const zcu = pt.zcu;
  18316     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18317     const src = block.nodeOffset(inst_data.src_node);
  18318     const ptr = try sema.resolveInst(inst_data.operand);
  18319     try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(zcu));
  18320     const loaded = try sema.analyzeLoad(block, src, ptr, src);
  18321     return sema.analyzeIsNonErr(block, src, loaded);
  18322 }
  18323 
  18324 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18325     const tracy = trace(@src());
  18326     defer tracy.end();
  18327 
  18328     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18329     const src = block.nodeOffset(inst_data.src_node);
  18330     const operand = try sema.resolveInst(inst_data.operand);
  18331     return sema.analyzeIsNonErr(block, src, operand);
  18332 }
  18333 
  18334 fn zirCondbr(
  18335     sema: *Sema,
  18336     parent_block: *Block,
  18337     inst: Zir.Inst.Index,
  18338 ) CompileError!void {
  18339     const tracy = trace(@src());
  18340     defer tracy.end();
  18341 
  18342     const pt = sema.pt;
  18343     const zcu = pt.zcu;
  18344     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18345     const cond_src = parent_block.src(.{ .node_offset_if_cond = inst_data.src_node });
  18346     const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
  18347 
  18348     const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
  18349     const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
  18350 
  18351     const uncasted_cond = try sema.resolveInst(extra.data.condition);
  18352     const cond = try sema.coerce(parent_block, .bool, uncasted_cond, cond_src);
  18353 
  18354     if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| {
  18355         const body = if (cond_val.toBool()) then_body else else_body;
  18356 
  18357         // We can propagate `.cold` hints from this branch since it's comptime-known
  18358         // to be taken from the parent branch.
  18359         const parent_hint = sema.branch_hint;
  18360         defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
  18361 
  18362         try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src);
  18363         // We use `analyzeBodyInner` since we want to propagate any comptime control flow to the caller.
  18364         return sema.analyzeBodyInner(parent_block, body);
  18365     }
  18366 
  18367     const gpa = sema.gpa;
  18368 
  18369     // We'll re-use the sub block to save on memory bandwidth, and yank out the
  18370     // instructions array in between using it for the then block and else block.
  18371     var sub_block = parent_block.makeSubBlock();
  18372     sub_block.runtime_loop = null;
  18373     sub_block.runtime_cond = cond_src;
  18374     sub_block.runtime_index.increment();
  18375     sub_block.need_debug_scope = null; // this body is emitted regardless
  18376     defer sub_block.instructions.deinit(gpa);
  18377 
  18378     const true_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
  18379     const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
  18380     defer gpa.free(true_instructions);
  18381 
  18382     const err_cond = blk: {
  18383         const index = extra.data.condition.toIndex() orelse break :blk null;
  18384         if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) break :blk null;
  18385 
  18386         const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node;
  18387         const err_operand = try sema.resolveInst(err_inst_data.operand);
  18388         const operand_ty = sema.typeOf(err_operand);
  18389         assert(operand_ty.zigTypeTag(zcu) == .error_union);
  18390         const result_ty = operand_ty.errorUnionSet(zcu);
  18391         break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);
  18392     };
  18393 
  18394     const false_hint: std.builtin.BranchHint = if (err_cond != null and
  18395         try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src, false))
  18396     h: {
  18397         // nothing to do here. weight against error branch
  18398         break :h .unlikely;
  18399     } else try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
  18400 
  18401     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
  18402         true_instructions.len + sub_block.instructions.items.len);
  18403     _ = try parent_block.addInst(.{
  18404         .tag = .cond_br,
  18405         .data = .{
  18406             .pl_op = .{
  18407                 .operand = cond,
  18408                 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18409                     .then_body_len = @intCast(true_instructions.len),
  18410                     .else_body_len = @intCast(sub_block.instructions.items.len),
  18411                     .branch_hints = .{
  18412                         .true = true_hint,
  18413                         .false = false_hint,
  18414                         // Code coverage is desired for error handling.
  18415                         .then_cov = .poi,
  18416                         .else_cov = .poi,
  18417                     },
  18418                 }),
  18419             },
  18420         },
  18421     });
  18422     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
  18423     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  18424 }
  18425 
  18426 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18427     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18428     const src = parent_block.nodeOffset(inst_data.src_node);
  18429     const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node });
  18430     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18431     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  18432     const err_union = try sema.resolveInst(extra.data.operand);
  18433     const err_union_ty = sema.typeOf(err_union);
  18434     const pt = sema.pt;
  18435     const zcu = pt.zcu;
  18436     if (err_union_ty.zigTypeTag(zcu) != .error_union) {
  18437         return sema.failWithOwnedErrorMsg(parent_block, msg: {
  18438             const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
  18439             errdefer msg.destroy(sema.gpa);
  18440             try sema.addDeclaredHereNote(msg, err_union_ty);
  18441             try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
  18442             break :msg msg;
  18443         });
  18444     }
  18445     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18446     if (is_non_err != .none) {
  18447         // We can propagate `.cold` hints from this branch since it's comptime-known
  18448         // to be taken from the parent branch.
  18449         const parent_hint = sema.branch_hint;
  18450         defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
  18451 
  18452         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18453         if (is_non_err_val.toBool()) {
  18454             return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
  18455         }
  18456         // We can analyze the body directly in the parent block because we know there are
  18457         // no breaks from the body possible, and that the body is noreturn.
  18458         try sema.analyzeBodyInner(parent_block, body);
  18459         return .unreachable_value;
  18460     }
  18461 
  18462     var sub_block = parent_block.makeSubBlock();
  18463     defer sub_block.instructions.deinit(sema.gpa);
  18464 
  18465     const parent_hint = sema.branch_hint;
  18466     defer sema.branch_hint = parent_hint;
  18467 
  18468     // This body is guaranteed to end with noreturn and has no breaks.
  18469     try sema.analyzeBodyInner(&sub_block, body);
  18470 
  18471     // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`.
  18472     const is_cold = sema.branch_hint == .cold;
  18473 
  18474     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).@"struct".fields.len +
  18475         sub_block.instructions.items.len);
  18476     const try_inst = try parent_block.addInst(.{
  18477         .tag = if (is_cold) .try_cold else .@"try",
  18478         .data = .{ .pl_op = .{
  18479             .operand = err_union,
  18480             .payload = sema.addExtraAssumeCapacity(Air.Try{
  18481                 .body_len = @intCast(sub_block.instructions.items.len),
  18482             }),
  18483         } },
  18484     });
  18485     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  18486     return try_inst;
  18487 }
  18488 
  18489 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18490     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18491     const src = parent_block.nodeOffset(inst_data.src_node);
  18492     const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node });
  18493     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18494     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  18495     const operand = try sema.resolveInst(extra.data.operand);
  18496     const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
  18497     const err_union_ty = sema.typeOf(err_union);
  18498     const pt = sema.pt;
  18499     const zcu = pt.zcu;
  18500     if (err_union_ty.zigTypeTag(zcu) != .error_union) {
  18501         return sema.failWithOwnedErrorMsg(parent_block, msg: {
  18502             const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
  18503             errdefer msg.destroy(sema.gpa);
  18504             try sema.addDeclaredHereNote(msg, err_union_ty);
  18505             try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
  18506             break :msg msg;
  18507         });
  18508     }
  18509     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18510     if (is_non_err != .none) {
  18511         // We can propagate `.cold` hints from this branch since it's comptime-known
  18512         // to be taken from the parent branch.
  18513         const parent_hint = sema.branch_hint;
  18514         defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
  18515 
  18516         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18517         if (is_non_err_val.toBool()) {
  18518             return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
  18519         }
  18520         // We can analyze the body directly in the parent block because we know there are
  18521         // no breaks from the body possible, and that the body is noreturn.
  18522         try sema.analyzeBodyInner(parent_block, body);
  18523         return .unreachable_value;
  18524     }
  18525 
  18526     var sub_block = parent_block.makeSubBlock();
  18527     defer sub_block.instructions.deinit(sema.gpa);
  18528 
  18529     const parent_hint = sema.branch_hint;
  18530     defer sema.branch_hint = parent_hint;
  18531 
  18532     // This body is guaranteed to end with noreturn and has no breaks.
  18533     try sema.analyzeBodyInner(&sub_block, body);
  18534 
  18535     // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`.
  18536     const is_cold = sema.branch_hint == .cold;
  18537 
  18538     const operand_ty = sema.typeOf(operand);
  18539     const ptr_info = operand_ty.ptrInfo(zcu);
  18540     const res_ty = try pt.ptrTypeSema(.{
  18541         .child = err_union_ty.errorUnionPayload(zcu).toIntern(),
  18542         .flags = .{
  18543             .is_const = ptr_info.flags.is_const,
  18544             .is_volatile = ptr_info.flags.is_volatile,
  18545             .is_allowzero = ptr_info.flags.is_allowzero,
  18546             .address_space = ptr_info.flags.address_space,
  18547         },
  18548     });
  18549     const res_ty_ref = Air.internedToRef(res_ty.toIntern());
  18550     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).@"struct".fields.len +
  18551         sub_block.instructions.items.len);
  18552     const try_inst = try parent_block.addInst(.{
  18553         .tag = if (is_cold) .try_ptr_cold else .try_ptr,
  18554         .data = .{ .ty_pl = .{
  18555             .ty = res_ty_ref,
  18556             .payload = sema.addExtraAssumeCapacity(Air.TryPtr{
  18557                 .ptr = operand,
  18558                 .body_len = @intCast(sub_block.instructions.items.len),
  18559             }),
  18560         } },
  18561     });
  18562     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  18563     return try_inst;
  18564 }
  18565 
  18566 fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*LabeledBlock {
  18567     const gop = sema.inst_map.getOrPutAssumeCapacity(dest_block);
  18568     if (gop.found_existing) existing: {
  18569         // This may be a *result* from an earlier iteration of an inline loop.
  18570         // In this case, there will not be a post-hoc block entry, and we can
  18571         // continue with the logic below.
  18572         const new_block_inst = gop.value_ptr.*.toIndex() orelse break :existing;
  18573         return sema.post_hoc_blocks.get(new_block_inst) orelse break :existing;
  18574     }
  18575 
  18576     try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1);
  18577 
  18578     const new_block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  18579     gop.value_ptr.* = new_block_inst.toRef();
  18580     try sema.air_instructions.append(sema.gpa, .{
  18581         .tag = .block,
  18582         .data = undefined,
  18583     });
  18584     const labeled_block = try sema.gpa.create(LabeledBlock);
  18585     labeled_block.* = .{
  18586         .label = .{
  18587             .zir_block = dest_block,
  18588             .merges = .{
  18589                 .src_locs = .{},
  18590                 .results = .{},
  18591                 .br_list = .{},
  18592                 .block_inst = new_block_inst,
  18593             },
  18594         },
  18595         .block = .{
  18596             .parent = block,
  18597             .sema = sema,
  18598             .namespace = block.namespace,
  18599             .instructions = .{},
  18600             .label = &labeled_block.label,
  18601             .inlining = block.inlining,
  18602             .comptime_reason = block.comptime_reason,
  18603             .src_base_inst = block.src_base_inst,
  18604             .type_name_ctx = block.type_name_ctx,
  18605         },
  18606     };
  18607     sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block);
  18608     return labeled_block;
  18609 }
  18610 
  18611 /// A `break` statement is inside a runtime condition, but trying to
  18612 /// break from an inline loop. In such case we must convert it to
  18613 /// a runtime break.
  18614 fn addRuntimeBreak(sema: *Sema, child_block: *Block, block_inst: Zir.Inst.Index, break_operand: Zir.Inst.Ref) !void {
  18615     const labeled_block = try sema.ensurePostHoc(child_block, block_inst);
  18616 
  18617     const operand = try sema.resolveInst(break_operand);
  18618     const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand);
  18619 
  18620     try labeled_block.label.merges.results.append(sema.gpa, operand);
  18621     try labeled_block.label.merges.br_list.append(sema.gpa, br_ref.toIndex().?);
  18622     try labeled_block.label.merges.src_locs.append(sema.gpa, null);
  18623 
  18624     labeled_block.block.runtime_index.increment();
  18625     if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) {
  18626         labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop;
  18627         labeled_block.block.runtime_loop = child_block.runtime_loop;
  18628     }
  18629 }
  18630 
  18631 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18632     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
  18633     const src = block.nodeOffset(inst_data.src_node);
  18634 
  18635     if (block.isComptime()) {
  18636         return sema.fail(block, src, "reached unreachable code", .{});
  18637     }
  18638     // TODO Add compile error for @optimizeFor occurring too late in a scope.
  18639     sema.analyzeUnreachable(block, src, true) catch |err| switch (err) {
  18640         error.AnalysisFail => {
  18641             const msg = sema.err orelse return err;
  18642             if (!mem.eql(u8, msg.msg, "runtime safety check not allowed in naked function")) return err;
  18643             try sema.errNote(src, msg, "the end of a naked function is implicitly unreachable", .{});
  18644             return err;
  18645         },
  18646         else => |e| return e,
  18647     };
  18648 }
  18649 
  18650 fn zirRetErrValue(
  18651     sema: *Sema,
  18652     block: *Block,
  18653     inst: Zir.Inst.Index,
  18654 ) CompileError!void {
  18655     const pt = sema.pt;
  18656     const zcu = pt.zcu;
  18657     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
  18658     const src = block.tokenOffset(inst_data.src_tok);
  18659     const err_name = try zcu.intern_pool.getOrPutString(
  18660         sema.gpa,
  18661         pt.tid,
  18662         inst_data.get(sema.code),
  18663         .no_embedded_nulls,
  18664     );
  18665     _ = try pt.getErrorValue(err_name);
  18666     // Return the error code from the function.
  18667     const error_set_type = try pt.singleErrorSetType(err_name);
  18668     const result_inst = Air.internedToRef((try pt.intern(.{ .err = .{
  18669         .ty = error_set_type.toIntern(),
  18670         .name = err_name,
  18671     } })));
  18672     return sema.analyzeRet(block, result_inst, src, src);
  18673 }
  18674 
  18675 fn zirRetImplicit(
  18676     sema: *Sema,
  18677     block: *Block,
  18678     inst: Zir.Inst.Index,
  18679 ) CompileError!void {
  18680     const tracy = trace(@src());
  18681     defer tracy.end();
  18682 
  18683     const pt = sema.pt;
  18684     const zcu = pt.zcu;
  18685     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
  18686     const r_brace_src = block.tokenOffset(inst_data.src_tok);
  18687     if (block.inlining == null and sema.func_is_naked) {
  18688         assert(!block.isComptime());
  18689         if (block.wantSafety()) {
  18690             // Calling a safety function from a naked function would not be legal.
  18691             _ = try block.addNoOp(.trap);
  18692         } else {
  18693             try sema.analyzeUnreachable(block, r_brace_src, false);
  18694         }
  18695         return;
  18696     }
  18697 
  18698     const operand = try sema.resolveInst(inst_data.operand);
  18699     const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = .zero });
  18700     const base_tag = sema.fn_ret_ty.baseZigTypeTag(zcu);
  18701     if (base_tag == .noreturn) {
  18702         const msg = msg: {
  18703             const msg = try sema.errMsg(ret_ty_src, "function declared '{f}' implicitly returns", .{
  18704                 sema.fn_ret_ty.fmt(pt),
  18705             });
  18706             errdefer msg.destroy(sema.gpa);
  18707             try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{});
  18708             break :msg msg;
  18709         };
  18710         return sema.failWithOwnedErrorMsg(block, msg);
  18711     } else if (base_tag != .void) {
  18712         const msg = msg: {
  18713             const msg = try sema.errMsg(ret_ty_src, "function with non-void return type '{f}' implicitly returns", .{
  18714                 sema.fn_ret_ty.fmt(pt),
  18715             });
  18716             errdefer msg.destroy(sema.gpa);
  18717             try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{});
  18718             break :msg msg;
  18719         };
  18720         return sema.failWithOwnedErrorMsg(block, msg);
  18721     }
  18722 
  18723     return sema.analyzeRet(block, operand, r_brace_src, r_brace_src);
  18724 }
  18725 
  18726 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18727     const tracy = trace(@src());
  18728     defer tracy.end();
  18729 
  18730     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18731     const operand = try sema.resolveInst(inst_data.operand);
  18732     const src = block.nodeOffset(inst_data.src_node);
  18733 
  18734     return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node }));
  18735 }
  18736 
  18737 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18738     const tracy = trace(@src());
  18739     defer tracy.end();
  18740 
  18741     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18742     const src = block.nodeOffset(inst_data.src_node);
  18743     const ret_ptr = try sema.resolveInst(inst_data.operand);
  18744 
  18745     if (block.isComptime() or block.inlining != null or sema.func_is_naked) {
  18746         const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
  18747         return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node }));
  18748     }
  18749 
  18750     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  18751         const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
  18752         return sema.retWithErrTracing(block, src, is_non_err, .ret_load, ret_ptr);
  18753     }
  18754 
  18755     _ = try block.addUnOp(.ret_load, ret_ptr);
  18756 }
  18757 
  18758 fn retWithErrTracing(
  18759     sema: *Sema,
  18760     block: *Block,
  18761     src: LazySrcLoc,
  18762     is_non_err: Air.Inst.Ref,
  18763     ret_tag: Air.Inst.Tag,
  18764     operand: Air.Inst.Ref,
  18765 ) CompileError!void {
  18766     const pt = sema.pt;
  18767     const need_check = switch (is_non_err) {
  18768         .bool_true => {
  18769             _ = try block.addUnOp(ret_tag, operand);
  18770             return;
  18771         },
  18772         .bool_false => false,
  18773         else => true,
  18774     };
  18775 
  18776     // This means we're returning something that might be an error!
  18777     // This should only be possible with the `auto` cc, so we definitely have an error trace.
  18778     assert(pt.zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace);
  18779 
  18780     const gpa = sema.gpa;
  18781     const return_err_fn = Air.internedToRef(try sema.getBuiltin(src, .returnError));
  18782 
  18783     if (!need_check) {
  18784         try sema.callBuiltin(block, src, return_err_fn, .never_tail, &.{}, .@"error return");
  18785         _ = try block.addUnOp(ret_tag, operand);
  18786         return;
  18787     }
  18788 
  18789     var then_block = block.makeSubBlock();
  18790     defer then_block.instructions.deinit(gpa);
  18791     _ = try then_block.addUnOp(ret_tag, operand);
  18792 
  18793     var else_block = block.makeSubBlock();
  18794     defer else_block.instructions.deinit(gpa);
  18795     try sema.callBuiltin(&else_block, src, return_err_fn, .never_tail, &.{}, .@"error return");
  18796     _ = try else_block.addUnOp(ret_tag, operand);
  18797 
  18798     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
  18799         then_block.instructions.items.len + else_block.instructions.items.len +
  18800         @typeInfo(Air.Block).@"struct".fields.len + 1);
  18801 
  18802     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18803         .then_body_len = @intCast(then_block.instructions.items.len),
  18804         .else_body_len = @intCast(else_block.instructions.items.len),
  18805         .branch_hints = .{
  18806             // Weight against error branch.
  18807             .true = .likely,
  18808             .false = .unlikely,
  18809             // Code coverage is not valuable on either branch.
  18810             .then_cov = .none,
  18811             .else_cov = .none,
  18812         },
  18813     });
  18814     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
  18815     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
  18816 
  18817     _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  18818         .operand = is_non_err,
  18819         .payload = cond_br_payload,
  18820     } } });
  18821 }
  18822 
  18823 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
  18824     const pt = sema.pt;
  18825     const zcu = pt.zcu;
  18826     return fn_ret_ty.isError(zcu) and zcu.comp.config.any_error_tracing;
  18827 }
  18828 
  18829 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18830     const pt = sema.pt;
  18831     const zcu = pt.zcu;
  18832     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index;
  18833 
  18834     if (!block.ownerModule().error_tracing) return;
  18835 
  18836     // This is only relevant at runtime.
  18837     if (block.isComptime() or block.is_typeof) return;
  18838 
  18839     const save_index = inst_data.operand == .none or b: {
  18840         const operand = try sema.resolveInst(inst_data.operand);
  18841         const operand_ty = sema.typeOf(operand);
  18842         break :b operand_ty.isError(zcu);
  18843     };
  18844 
  18845     if (save_index)
  18846         block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block);
  18847 }
  18848 
  18849 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
  18850     const extra = sema.code.extraData(Zir.Inst.RestoreErrRetIndex, extended.operand).data;
  18851     return sema.restoreErrRetIndex(start_block, start_block.nodeOffset(extra.src_node), extra.block, extra.operand);
  18852 }
  18853 
  18854 /// If `operand` is non-error (or is `none`), restores the error return trace to
  18855 /// its state at the point `block` was reached (or, if `block` is `none`, the
  18856 /// point this function began execution).
  18857 fn restoreErrRetIndex(sema: *Sema, start_block: *Block, src: LazySrcLoc, target_block: Zir.Inst.Ref, operand_zir: Zir.Inst.Ref) CompileError!void {
  18858     const tracy = trace(@src());
  18859     defer tracy.end();
  18860 
  18861     const pt = sema.pt;
  18862     const zcu = pt.zcu;
  18863 
  18864     const saved_index = if (target_block.toIndexAllowNone()) |zir_block| b: {
  18865         var block = start_block;
  18866         while (true) {
  18867             if (block.label) |label| {
  18868                 if (label.zir_block == zir_block) {
  18869                     const target_trace_index = if (block.parent) |parent_block| tgt: {
  18870                         break :tgt parent_block.error_return_trace_index;
  18871                     } else sema.error_return_trace_index_on_fn_entry;
  18872 
  18873                     if (start_block.error_return_trace_index != target_trace_index)
  18874                         break :b target_trace_index;
  18875 
  18876                     return; // No need to restore
  18877                 }
  18878             }
  18879             block = block.parent.?;
  18880         }
  18881     } else b: {
  18882         if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry)
  18883             break :b sema.error_return_trace_index_on_fn_entry;
  18884 
  18885         return; // No need to restore
  18886     };
  18887 
  18888     const operand = try sema.resolveInstAllowNone(operand_zir);
  18889 
  18890     if (start_block.isComptime() or start_block.is_typeof) {
  18891         const is_non_error = if (operand != .none) blk: {
  18892             const is_non_error_inst = try sema.analyzeIsNonErr(start_block, src, operand);
  18893             const cond_val = try sema.resolveDefinedValue(start_block, src, is_non_error_inst);
  18894             break :blk cond_val.?.toBool();
  18895         } else true; // no operand means pop unconditionally
  18896 
  18897         if (is_non_error) return;
  18898 
  18899         const saved_index_val = try sema.resolveDefinedValue(start_block, src, saved_index);
  18900         const saved_index_int = saved_index_val.?.toUnsignedInt(zcu);
  18901         assert(saved_index_int <= sema.comptime_err_ret_trace.items.len);
  18902         sema.comptime_err_ret_trace.items.len = @intCast(saved_index_int);
  18903         return;
  18904     }
  18905 
  18906     if (!zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace) return;
  18907     if (!start_block.ownerModule().error_tracing) return;
  18908 
  18909     assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere
  18910 
  18911     return sema.popErrorReturnTrace(start_block, src, operand, saved_index);
  18912 }
  18913 
  18914 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
  18915     const pt = sema.pt;
  18916     const zcu = pt.zcu;
  18917     const ip = &zcu.intern_pool;
  18918     assert(sema.fn_ret_ty.zigTypeTag(zcu) == .error_union);
  18919     const err_set_ty = sema.fn_ret_ty.errorUnionSet(zcu).toIntern();
  18920     switch (err_set_ty) {
  18921         .adhoc_inferred_error_set_type => {
  18922             const ies = sema.fn_ret_ty_ies.?;
  18923             assert(ies.func == .none);
  18924             try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand));
  18925         },
  18926         else => if (ip.isInferredErrorSetType(err_set_ty)) {
  18927             const ies = sema.fn_ret_ty_ies.?;
  18928             assert(ies.func == sema.owner.unwrap().func);
  18929             try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand));
  18930         },
  18931     }
  18932 }
  18933 
  18934 fn addToInferredErrorSetPtr(sema: *Sema, ies: *InferredErrorSet, op_ty: Type) !void {
  18935     const arena = sema.arena;
  18936     const pt = sema.pt;
  18937     const zcu = pt.zcu;
  18938     const ip = &zcu.intern_pool;
  18939     switch (op_ty.zigTypeTag(zcu)) {
  18940         .error_set => try ies.addErrorSet(op_ty, ip, arena),
  18941         .error_union => try ies.addErrorSet(op_ty.errorUnionSet(zcu), ip, arena),
  18942         else => {},
  18943     }
  18944 }
  18945 
  18946 fn analyzeRet(
  18947     sema: *Sema,
  18948     block: *Block,
  18949     uncasted_operand: Air.Inst.Ref,
  18950     src: LazySrcLoc,
  18951     operand_src: LazySrcLoc,
  18952 ) CompileError!void {
  18953     // Special case for returning an error to an inferred error set; we need to
  18954     // add the error tag to the inferred error set of the in-scope function, so
  18955     // that the coercion below works correctly.
  18956     const pt = sema.pt;
  18957     const zcu = pt.zcu;
  18958     if (sema.fn_ret_ty_ies != null and sema.fn_ret_ty.zigTypeTag(zcu) == .error_union) {
  18959         try sema.addToInferredErrorSet(uncasted_operand);
  18960     }
  18961     const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, operand_src, .{ .is_ret = true }) catch |err| switch (err) {
  18962         error.NotCoercible => unreachable,
  18963         else => |e| return e,
  18964     };
  18965 
  18966     if (block.inlining) |inlining| {
  18967         assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr
  18968         if (block.isComptime()) {
  18969             const ret_val = try sema.resolveConstValue(block, operand_src, operand, null);
  18970             inlining.comptime_result = operand;
  18971 
  18972             if (sema.fn_ret_ty.isError(zcu) and ret_val.getErrorName(zcu) != .none) {
  18973                 try sema.comptime_err_ret_trace.append(src);
  18974             }
  18975             return error.ComptimeReturn;
  18976         }
  18977         // We are inlining a function call; rewrite the `ret` as a `break`.
  18978         const br_inst = try block.addBr(inlining.merges.block_inst, operand);
  18979         try inlining.merges.results.append(sema.gpa, operand);
  18980         try inlining.merges.br_list.append(sema.gpa, br_inst.toIndex().?);
  18981         try inlining.merges.src_locs.append(sema.gpa, operand_src);
  18982         return;
  18983     } else if (block.isComptime()) {
  18984         return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
  18985     } else if (sema.func_is_naked) {
  18986         const msg = msg: {
  18987             const msg = try sema.errMsg(src, "cannot return from naked function", .{});
  18988             errdefer msg.destroy(sema.gpa);
  18989 
  18990             try sema.errNote(src, msg, "can only return using assembly", .{});
  18991             break :msg msg;
  18992         };
  18993         return sema.failWithOwnedErrorMsg(block, msg);
  18994     }
  18995 
  18996     try sema.fn_ret_ty.resolveLayout(pt);
  18997 
  18998     try sema.validateRuntimeValue(block, operand_src, operand);
  18999 
  19000     const air_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret;
  19001     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  19002         // Avoid adding a frame to the error return trace in case the value is comptime-known
  19003         // to be not an error.
  19004         const is_non_err = try sema.analyzeIsNonErr(block, operand_src, operand);
  19005         return sema.retWithErrTracing(block, src, is_non_err, air_tag, operand);
  19006     }
  19007 
  19008     _ = try block.addUnOp(air_tag, operand);
  19009 }
  19010 
  19011 fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
  19012     // extend this swich as additional operators are implemented
  19013     return switch (tag) {
  19014         .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,
  19015         else => false,
  19016     };
  19017 }
  19018 
  19019 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19020     const tracy = trace(@src());
  19021     defer tracy.end();
  19022 
  19023     const pt = sema.pt;
  19024     const zcu = pt.zcu;
  19025     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].ptr_type;
  19026     const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index);
  19027     const elem_ty_src = block.src(.{ .node_offset_ptr_elem = extra.data.src_node });
  19028     const sentinel_src = block.src(.{ .node_offset_ptr_sentinel = extra.data.src_node });
  19029     const align_src = block.src(.{ .node_offset_ptr_align = extra.data.src_node });
  19030     const addrspace_src = block.src(.{ .node_offset_ptr_addrspace = extra.data.src_node });
  19031     const bitoffset_src = block.src(.{ .node_offset_ptr_bitoffset = extra.data.src_node });
  19032     const hostsize_src = block.src(.{ .node_offset_ptr_hostsize = extra.data.src_node });
  19033 
  19034     const elem_ty = blk: {
  19035         const air_inst = try sema.resolveInst(extra.data.elem_type);
  19036         const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| {
  19037             if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(zcu)) {
  19038                 try sema.errNote(elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{});
  19039             }
  19040             return err;
  19041         };
  19042         assert(!ty.isGenericPoison());
  19043         break :blk ty;
  19044     };
  19045 
  19046     if (elem_ty.zigTypeTag(zcu) == .noreturn)
  19047         return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{});
  19048 
  19049     const target = zcu.getTarget();
  19050 
  19051     var extra_i = extra.end;
  19052 
  19053     const sentinel = if (inst_data.flags.has_sentinel) blk: {
  19054         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  19055         extra_i += 1;
  19056         const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src);
  19057         const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{ .simple = .pointer_sentinel });
  19058         try checkSentinelType(sema, block, sentinel_src, elem_ty);
  19059         break :blk val.toIntern();
  19060     } else .none;
  19061 
  19062     const abi_align: Alignment = if (inst_data.flags.has_align) blk: {
  19063         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  19064         extra_i += 1;
  19065         const coerced = try sema.coerce(block, align_ty, try sema.resolveInst(ref), align_src);
  19066         const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" });
  19067         // Check if this happens to be the lazy alignment of our element type, in
  19068         // which case we can make this 0 without resolving it.
  19069         switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  19070             .int => |int| switch (int.storage) {
  19071                 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none,
  19072                 else => {},
  19073             },
  19074             else => {},
  19075         }
  19076         const align_bytes = (try val.getUnsignedIntSema(pt)).?;
  19077         break :blk try sema.validateAlign(block, align_src, align_bytes);
  19078     } else .none;
  19079 
  19080     const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: {
  19081         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  19082         extra_i += 1;
  19083         break :blk try sema.resolveAddressSpace(block, addrspace_src, ref, .pointer);
  19084     } else if (elem_ty.zigTypeTag(zcu) == .@"fn" and target.cpu.arch == .avr) .flash else .generic;
  19085 
  19086     const bit_offset: u16 = if (inst_data.flags.has_bit_range) blk: {
  19087         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  19088         extra_i += 1;
  19089         const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, .u16, .{ .simple = .type });
  19090         break :blk @intCast(bit_offset);
  19091     } else 0;
  19092 
  19093     const host_size: u16 = if (inst_data.flags.has_bit_range) blk: {
  19094         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  19095         extra_i += 1;
  19096         const host_size = try sema.resolveInt(block, hostsize_src, ref, .u16, .{ .simple = .type });
  19097         break :blk @intCast(host_size);
  19098     } else 0;
  19099 
  19100     if (host_size != 0) {
  19101         if (bit_offset >= host_size * 8) {
  19102             return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} starts {d} bits after the end of a {d} byte host integer", .{
  19103                 elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size,
  19104             });
  19105         }
  19106         const elem_bit_size = try elem_ty.bitSizeSema(pt);
  19107         if (elem_bit_size > host_size * 8 - bit_offset) {
  19108             return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} ends {d} bits after the end of a {d} byte host integer", .{
  19109                 elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size,
  19110             });
  19111         }
  19112     }
  19113 
  19114     if (elem_ty.zigTypeTag(zcu) == .@"fn") {
  19115         if (inst_data.size != .one) {
  19116             return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{});
  19117         }
  19118     } else if (inst_data.size == .many and elem_ty.zigTypeTag(zcu) == .@"opaque") {
  19119         return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
  19120     } else if (inst_data.size == .c) {
  19121         if (!try sema.validateExternType(elem_ty, .other)) {
  19122             const msg = msg: {
  19123                 const msg = try sema.errMsg(elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)});
  19124                 errdefer msg.destroy(sema.gpa);
  19125 
  19126                 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src, elem_ty, .other);
  19127 
  19128                 try sema.addDeclaredHereNote(msg, elem_ty);
  19129                 break :msg msg;
  19130             };
  19131             return sema.failWithOwnedErrorMsg(block, msg);
  19132         }
  19133         if (elem_ty.zigTypeTag(zcu) == .@"opaque") {
  19134             return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{});
  19135         }
  19136     }
  19137 
  19138     if (host_size != 0 and !try sema.validatePackedType(elem_ty)) {
  19139         return sema.failWithOwnedErrorMsg(block, msg: {
  19140             const msg = try sema.errMsg(elem_ty_src, "bit-pointer cannot refer to value of type '{f}'", .{elem_ty.fmt(pt)});
  19141             errdefer msg.destroy(sema.gpa);
  19142             try sema.explainWhyTypeIsNotPacked(msg, elem_ty_src, elem_ty);
  19143             break :msg msg;
  19144         });
  19145     }
  19146 
  19147     const ty = try pt.ptrTypeSema(.{
  19148         .child = elem_ty.toIntern(),
  19149         .sentinel = sentinel,
  19150         .flags = .{
  19151             .alignment = abi_align,
  19152             .address_space = address_space,
  19153             .is_const = !inst_data.flags.is_mutable,
  19154             .is_allowzero = inst_data.flags.is_allowzero,
  19155             .is_volatile = inst_data.flags.is_volatile,
  19156             .size = inst_data.size,
  19157         },
  19158         .packed_offset = .{
  19159             .bit_offset = bit_offset,
  19160             .host_size = host_size,
  19161         },
  19162     });
  19163     return Air.internedToRef(ty.toIntern());
  19164 }
  19165 
  19166 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19167     const tracy = trace(@src());
  19168     defer tracy.end();
  19169 
  19170     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  19171     const src = block.nodeOffset(inst_data.src_node);
  19172     const ty_src = block.src(.{ .node_offset_init_ty = inst_data.src_node });
  19173     const obj_ty = try sema.resolveType(block, ty_src, inst_data.operand);
  19174     const pt = sema.pt;
  19175     const zcu = pt.zcu;
  19176 
  19177     switch (obj_ty.zigTypeTag(zcu)) {
  19178         .@"struct" => return sema.structInitEmpty(block, obj_ty, src, src),
  19179         .array, .vector => return sema.arrayInitEmpty(block, src, obj_ty),
  19180         .void => return Air.internedToRef(Value.void.toIntern()),
  19181         .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}),
  19182         else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
  19183     }
  19184 }
  19185 
  19186 fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_byref: bool) CompileError!Air.Inst.Ref {
  19187     const tracy = trace(@src());
  19188     defer tracy.end();
  19189 
  19190     const pt = sema.pt;
  19191     const zcu = pt.zcu;
  19192     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  19193     const src = block.nodeOffset(inst_data.src_node);
  19194 
  19195     // Generic poison means this is an untyped anonymous empty struct/array init
  19196     const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse {
  19197         if (is_byref) {
  19198             return sema.uavRef(.empty_tuple);
  19199         } else {
  19200             return .empty_tuple;
  19201         }
  19202     };
  19203 
  19204     const init_ty = if (is_byref) ty: {
  19205         const ptr_ty = ty_operand.optEuBaseType(zcu);
  19206         assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
  19207         switch (ptr_ty.ptrSize(zcu)) {
  19208             // Use a zero-length array for a slice or many-ptr result
  19209             .slice, .many => break :ty try pt.arrayType(.{
  19210                 .len = 0,
  19211                 .child = ptr_ty.childType(zcu).toIntern(),
  19212                 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  19213             }),
  19214             // Just use the child type for a single-pointer or C-pointer result
  19215             .one, .c => {
  19216                 const child = ptr_ty.childType(zcu);
  19217                 if (child.toIntern() == .anyopaque_type) {
  19218                     // ...unless that child is anyopaque, in which case this is equivalent to an untyped init.
  19219                     // `.{}` is an empty tuple.
  19220                     if (is_byref) {
  19221                         return sema.uavRef(.empty_tuple);
  19222                     } else {
  19223                         return .empty_tuple;
  19224                     }
  19225                 }
  19226                 break :ty child;
  19227             },
  19228         }
  19229         if (!ptr_ty.isSlice(zcu)) {
  19230             break :ty ptr_ty.childType(zcu);
  19231         }
  19232         // To make `&.{}` a `[:s]T`, the init should be a `[0:s]T`.
  19233         break :ty try pt.arrayType(.{
  19234             .len = 0,
  19235             .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  19236             .child = ptr_ty.childType(zcu).toIntern(),
  19237         });
  19238     } else ty_operand;
  19239     const obj_ty = init_ty.optEuBaseType(zcu);
  19240 
  19241     const empty_ref = switch (obj_ty.zigTypeTag(zcu)) {
  19242         .@"struct" => try sema.structInitEmpty(block, obj_ty, src, src),
  19243         .array, .vector => try sema.arrayInitEmpty(block, src, obj_ty),
  19244         .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}),
  19245         else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
  19246     };
  19247     const init_ref = try sema.coerce(block, init_ty, empty_ref, src);
  19248 
  19249     if (is_byref) {
  19250         const init_val = (try sema.resolveValue(init_ref)).?;
  19251         return sema.uavRef(init_val.toIntern());
  19252     } else {
  19253         return init_ref;
  19254     }
  19255 }
  19256 
  19257 fn structInitEmpty(
  19258     sema: *Sema,
  19259     block: *Block,
  19260     struct_ty: Type,
  19261     dest_src: LazySrcLoc,
  19262     init_src: LazySrcLoc,
  19263 ) CompileError!Air.Inst.Ref {
  19264     const pt = sema.pt;
  19265     const zcu = pt.zcu;
  19266     const gpa = sema.gpa;
  19267     // This logic must be synchronized with that in `zirStructInit`.
  19268     try struct_ty.resolveFields(pt);
  19269 
  19270     // The init values to use for the struct instance.
  19271     const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(zcu));
  19272     defer gpa.free(field_inits);
  19273     @memset(field_inits, .none);
  19274 
  19275     // Maps field index in the struct declaration to the field index in the initialization expression.
  19276     const field_assign_idxs = try gpa.alloc(?usize, struct_ty.structFieldCount(zcu));
  19277     defer gpa.free(field_assign_idxs);
  19278     @memset(field_assign_idxs, null);
  19279 
  19280     return sema.finishStructInit(block, init_src, dest_src, field_inits, field_assign_idxs, struct_ty, struct_ty, false);
  19281 }
  19282 
  19283 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref {
  19284     const pt = sema.pt;
  19285     const zcu = pt.zcu;
  19286     const arr_len = obj_ty.arrayLen(zcu);
  19287     if (arr_len != 0) {
  19288         if (obj_ty.zigTypeTag(zcu) == .array) {
  19289             return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len});
  19290         } else {
  19291             return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len});
  19292         }
  19293     }
  19294     return .fromValue(try pt.aggregateValue(obj_ty, &.{}));
  19295 }
  19296 
  19297 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19298     const pt = sema.pt;
  19299     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19300     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  19301     const field_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  19302     const init_src = block.builtinCallArgSrc(inst_data.src_node, 2);
  19303     const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
  19304     const union_ty = try sema.resolveType(block, ty_src, extra.union_type);
  19305     if (union_ty.zigTypeTag(pt.zcu) != .@"union") {
  19306         return sema.fail(block, ty_src, "expected union type, found '{f}'", .{union_ty.fmt(pt)});
  19307     }
  19308     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .union_field_name });
  19309     const init = try sema.resolveInst(extra.init);
  19310     return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src);
  19311 }
  19312 
  19313 fn unionInit(
  19314     sema: *Sema,
  19315     block: *Block,
  19316     uncasted_init: Air.Inst.Ref,
  19317     init_src: LazySrcLoc,
  19318     union_ty: Type,
  19319     union_ty_src: LazySrcLoc,
  19320     field_name: InternPool.NullTerminatedString,
  19321     field_src: LazySrcLoc,
  19322 ) CompileError!Air.Inst.Ref {
  19323     const pt = sema.pt;
  19324     const zcu = pt.zcu;
  19325     const ip = &zcu.intern_pool;
  19326     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
  19327     const field_ty: Type = .fromInterned(zcu.typeToUnion(union_ty).?.field_types.get(ip)[field_index]);
  19328     const init = try sema.coerce(block, field_ty, uncasted_init, init_src);
  19329     _ = union_ty_src;
  19330     return unionInitFromEnumTag(sema, block, init_src, union_ty, field_index, init);
  19331 }
  19332 
  19333 fn unionInitFromEnumTag(
  19334     sema: *Sema,
  19335     block: *Block,
  19336     init_src: LazySrcLoc,
  19337     union_ty: Type,
  19338     field_index: u32,
  19339     init: Air.Inst.Ref,
  19340 ) !Air.Inst.Ref {
  19341     const pt = sema.pt;
  19342     const zcu = pt.zcu;
  19343 
  19344     if (try sema.resolveValue(init)) |init_val| {
  19345         const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
  19346         const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
  19347         return Air.internedToRef((try pt.internUnion(.{
  19348             .ty = union_ty.toIntern(),
  19349             .tag = tag_val.toIntern(),
  19350             .val = init_val.toIntern(),
  19351         })));
  19352     }
  19353 
  19354     try sema.requireRuntimeBlock(block, init_src, null);
  19355     return block.addUnionInit(union_ty, field_index, init);
  19356 }
  19357 
  19358 fn zirStructInit(
  19359     sema: *Sema,
  19360     block: *Block,
  19361     inst: Zir.Inst.Index,
  19362     is_ref: bool,
  19363 ) CompileError!Air.Inst.Ref {
  19364     const gpa = sema.gpa;
  19365     const zir_datas = sema.code.instructions.items(.data);
  19366     const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
  19367     const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
  19368     const src = block.nodeOffset(inst_data.src_node);
  19369 
  19370     const pt = sema.pt;
  19371     const zcu = pt.zcu;
  19372     const ip = &zcu.intern_pool;
  19373     const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
  19374     const first_field_type_data = zir_datas[@intFromEnum(first_item.field_type)].pl_node;
  19375     const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
  19376     const result_ty = try sema.resolveTypeOrPoison(block, src, first_field_type_extra.container_type) orelse {
  19377         // The type wasn't actually known, so treat this as an anon struct init.
  19378         return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref);
  19379     };
  19380     const resolved_ty = result_ty.optEuBaseType(zcu);
  19381     try resolved_ty.resolveLayout(pt);
  19382 
  19383     if (resolved_ty.zigTypeTag(zcu) == .@"struct") {
  19384         // This logic must be synchronized with that in `zirStructInitEmpty`.
  19385 
  19386         // Maps field index to field_type index of where it was already initialized.
  19387         // For making sure all fields are accounted for and no fields are duplicated.
  19388         const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(zcu));
  19389         defer gpa.free(found_fields);
  19390 
  19391         // The init values to use for the struct instance.
  19392         const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(zcu));
  19393         defer gpa.free(field_inits);
  19394         @memset(field_inits, .none);
  19395 
  19396         // Maps field index in the struct declaration to the field index in the initialization expression.
  19397         const field_assign_idxs = try gpa.alloc(?usize, resolved_ty.structFieldCount(zcu));
  19398         defer gpa.free(field_assign_idxs);
  19399         @memset(field_assign_idxs, null);
  19400 
  19401         var field_i: u32 = 0;
  19402         var extra_index = extra.end;
  19403 
  19404         const is_packed = resolved_ty.containerLayout(zcu) == .@"packed";
  19405         while (field_i < extra.data.fields_len) : (field_i += 1) {
  19406             const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
  19407             extra_index = item.end;
  19408 
  19409             const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
  19410             const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node });
  19411             const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  19412             const field_name = try ip.getOrPutString(
  19413                 gpa,
  19414                 pt.tid,
  19415                 sema.code.nullTerminatedString(field_type_extra.name_start),
  19416                 .no_embedded_nulls,
  19417             );
  19418             const field_index = if (resolved_ty.isTuple(zcu))
  19419                 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
  19420             else
  19421                 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
  19422             assert(field_inits[field_index] == .none);
  19423             field_assign_idxs[field_index] = field_i;
  19424             found_fields[field_index] = item.data.field_type;
  19425             const uncoerced_init = try sema.resolveInst(item.data.init);
  19426             const field_ty = resolved_ty.fieldType(field_index, zcu);
  19427             field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src);
  19428             if (!is_packed) {
  19429                 try resolved_ty.resolveStructFieldInits(pt);
  19430                 if (try resolved_ty.structFieldValueComptime(pt, field_index)) |default_value| {
  19431                     const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {
  19432                         return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field });
  19433                     };
  19434 
  19435                     if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) {
  19436                         return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
  19437                     }
  19438                 }
  19439             }
  19440         }
  19441 
  19442         return sema.finishStructInit(block, src, src, field_inits, field_assign_idxs, resolved_ty, result_ty, is_ref);
  19443     } else if (resolved_ty.zigTypeTag(zcu) == .@"union") {
  19444         if (extra.data.fields_len != 1) {
  19445             return sema.fail(block, src, "union initialization expects exactly one field", .{});
  19446         }
  19447 
  19448         const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end);
  19449 
  19450         const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
  19451         const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node });
  19452         const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  19453         const field_name = try ip.getOrPutString(
  19454             gpa,
  19455             pt.tid,
  19456             sema.code.nullTerminatedString(field_type_extra.name_start),
  19457             .no_embedded_nulls,
  19458         );
  19459         const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src);
  19460         const tag_ty = resolved_ty.unionTagTypeHypothetical(zcu);
  19461         const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
  19462         const field_ty: Type = .fromInterned(zcu.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index]);
  19463 
  19464         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  19465             return sema.failWithOwnedErrorMsg(block, msg: {
  19466                 const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{});
  19467                 errdefer msg.destroy(sema.gpa);
  19468 
  19469                 try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{f}' declared here", .{
  19470                     field_name.fmt(ip),
  19471                 });
  19472                 try sema.addDeclaredHereNote(msg, resolved_ty);
  19473                 break :msg msg;
  19474             });
  19475         }
  19476 
  19477         const uncoerced_init_inst = try sema.resolveInst(item.data.init);
  19478         const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);
  19479 
  19480         if (try sema.resolveValue(init_inst)) |val| {
  19481             const struct_val = Value.fromInterned(try pt.internUnion(.{
  19482                 .ty = resolved_ty.toIntern(),
  19483                 .tag = tag_val.toIntern(),
  19484                 .val = val.toIntern(),
  19485             }));
  19486             const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src);
  19487             const final_val = (try sema.resolveValue(final_val_inst)).?;
  19488             return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
  19489         }
  19490 
  19491         if (try resolved_ty.comptimeOnlySema(pt)) {
  19492             return sema.failWithNeededComptime(block, field_src, .{ .comptime_only = .{
  19493                 .ty = resolved_ty,
  19494                 .msg = .union_init,
  19495             } });
  19496         }
  19497 
  19498         try sema.validateRuntimeValue(block, field_src, init_inst);
  19499 
  19500         if (is_ref) {
  19501             const target = zcu.getTarget();
  19502             const alloc_ty = try pt.ptrTypeSema(.{
  19503                 .child = result_ty.toIntern(),
  19504                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19505             });
  19506             const alloc = try block.addTy(.alloc, alloc_ty);
  19507             const base_ptr = try sema.optEuBasePtrInit(block, alloc, src);
  19508             const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true);
  19509             try sema.storePtr(block, src, field_ptr, init_inst);
  19510             if ((try sema.typeHasOnePossibleValue(tag_ty)) == null) {
  19511                 const new_tag = Air.internedToRef(tag_val.toIntern());
  19512                 _ = try block.addBinOp(.set_union_tag, base_ptr, new_tag);
  19513             }
  19514             return sema.makePtrConst(block, alloc);
  19515         }
  19516 
  19517         try sema.requireRuntimeBlock(block, src, null);
  19518         const union_val = try block.addUnionInit(resolved_ty, field_index, init_inst);
  19519         return sema.coerce(block, result_ty, union_val, src);
  19520     }
  19521     unreachable;
  19522 }
  19523 
  19524 fn finishStructInit(
  19525     sema: *Sema,
  19526     block: *Block,
  19527     init_src: LazySrcLoc,
  19528     dest_src: LazySrcLoc,
  19529     field_inits: []Air.Inst.Ref,
  19530     field_assign_idxs: []?usize,
  19531     struct_ty: Type,
  19532     result_ty: Type,
  19533     is_ref: bool,
  19534 ) CompileError!Air.Inst.Ref {
  19535     const pt = sema.pt;
  19536     const zcu = pt.zcu;
  19537     const ip = &zcu.intern_pool;
  19538 
  19539     var root_msg: ?*Zcu.ErrorMsg = null;
  19540     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  19541 
  19542     switch (ip.indexToKey(struct_ty.toIntern())) {
  19543         .tuple_type => |tuple| {
  19544             // We can't get the slices, as the coercion may invalidate them.
  19545             for (0..tuple.types.len) |i| {
  19546                 if (field_inits[i] != .none) {
  19547                     // Coerce the init value to the field type.
  19548                     const field_src = block.src(.{ .init_elem = .{
  19549                         .init_node_offset = init_src.offset.node_offset.x,
  19550                         .elem_index = @intCast(i),
  19551                     } });
  19552                     const field_ty: Type = .fromInterned(tuple.types.get(ip)[i]);
  19553                     field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src);
  19554                     continue;
  19555                 }
  19556 
  19557                 const default_val = tuple.values.get(ip)[i];
  19558 
  19559                 if (default_val == .none) {
  19560                     const template = "missing tuple field with index {d}";
  19561                     if (root_msg) |msg| {
  19562                         try sema.errNote(init_src, msg, template, .{i});
  19563                     } else {
  19564                         root_msg = try sema.errMsg(init_src, template, .{i});
  19565                     }
  19566                 } else {
  19567                     field_inits[i] = Air.internedToRef(default_val);
  19568                 }
  19569             }
  19570         },
  19571         .struct_type => {
  19572             const struct_type = ip.loadStructType(struct_ty.toIntern());
  19573             for (0..struct_type.field_types.len) |i| {
  19574                 if (field_inits[i] != .none) {
  19575                     // Coerce the init value to the field type.
  19576                     const field_src = block.src(.{ .init_elem = .{
  19577                         .init_node_offset = init_src.offset.node_offset.x,
  19578                         .elem_index = @intCast(i),
  19579                     } });
  19580                     const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  19581                     field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src);
  19582                     continue;
  19583                 }
  19584 
  19585                 try struct_ty.resolveStructFieldInits(pt);
  19586 
  19587                 const field_init = struct_type.fieldInit(ip, i);
  19588                 if (field_init == .none) {
  19589                     const field_name = struct_type.field_names.get(ip)[i];
  19590                     const template = "missing struct field: {f}";
  19591                     const args = .{field_name.fmt(ip)};
  19592                     if (root_msg) |msg| {
  19593                         try sema.errNote(init_src, msg, template, args);
  19594                     } else {
  19595                         root_msg = try sema.errMsg(init_src, template, args);
  19596                     }
  19597                 } else {
  19598                     field_inits[i] = Air.internedToRef(field_init);
  19599                 }
  19600             }
  19601         },
  19602         else => unreachable,
  19603     }
  19604 
  19605     if (root_msg) |msg| {
  19606         try sema.addDeclaredHereNote(msg, struct_ty);
  19607         root_msg = null;
  19608         return sema.failWithOwnedErrorMsg(block, msg);
  19609     }
  19610 
  19611     // Find which field forces the expression to be runtime, if any.
  19612     const opt_runtime_index = for (field_inits, field_assign_idxs) |field_init, field_assign| {
  19613         if (!(try sema.isComptimeKnown(field_init))) {
  19614             break field_assign;
  19615         }
  19616     } else null;
  19617 
  19618     const runtime_index = opt_runtime_index orelse {
  19619         const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
  19620         for (elems, field_inits) |*elem, field_init| {
  19621             elem.* = (sema.resolveValue(field_init) catch unreachable).?.toIntern();
  19622         }
  19623         const struct_val = try pt.aggregateValue(struct_ty, elems);
  19624         const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), init_src);
  19625         const final_val = (try sema.resolveValue(final_val_inst)).?;
  19626         return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
  19627     };
  19628 
  19629     if (try struct_ty.comptimeOnlySema(pt)) {
  19630         return sema.failWithNeededComptime(block, block.src(.{ .init_elem = .{
  19631             .init_node_offset = init_src.offset.node_offset.x,
  19632             .elem_index = @intCast(runtime_index),
  19633         } }), .{ .comptime_only = .{
  19634             .ty = struct_ty,
  19635             .msg = .struct_init,
  19636         } });
  19637     }
  19638 
  19639     for (field_inits) |field_init| {
  19640         try sema.validateRuntimeValue(block, dest_src, field_init);
  19641     }
  19642 
  19643     if (is_ref) {
  19644         try struct_ty.resolveLayout(pt);
  19645         const target = zcu.getTarget();
  19646         const alloc_ty = try pt.ptrTypeSema(.{
  19647             .child = result_ty.toIntern(),
  19648             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19649         });
  19650         const alloc = try block.addTy(.alloc, alloc_ty);
  19651         const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src);
  19652         for (field_inits, 0..) |field_init, i_usize| {
  19653             const i: u32 = @intCast(i_usize);
  19654             const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, struct_ty);
  19655             try sema.storePtr(block, dest_src, field_ptr, field_init);
  19656         }
  19657 
  19658         return sema.makePtrConst(block, alloc);
  19659     }
  19660 
  19661     try sema.requireRuntimeBlock(block, dest_src, block.src(.{ .init_elem = .{
  19662         .init_node_offset = init_src.offset.node_offset.x,
  19663         .elem_index = @intCast(runtime_index),
  19664     } }));
  19665     try struct_ty.resolveStructFieldInits(pt);
  19666     const struct_val = try block.addAggregateInit(struct_ty, field_inits);
  19667     return sema.coerce(block, result_ty, struct_val, init_src);
  19668 }
  19669 
  19670 fn zirStructInitAnon(
  19671     sema: *Sema,
  19672     block: *Block,
  19673     inst: Zir.Inst.Index,
  19674 ) CompileError!Air.Inst.Ref {
  19675     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19676     const src = block.nodeOffset(inst_data.src_node);
  19677     const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
  19678     return sema.structInitAnon(block, src, inst, .anon_init, extra.data, extra.end, false);
  19679 }
  19680 
  19681 fn structInitAnon(
  19682     sema: *Sema,
  19683     block: *Block,
  19684     src: LazySrcLoc,
  19685     inst: Zir.Inst.Index,
  19686     /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a
  19687     /// generic poison type. In this case, we need to know to interpret the extra data differently.
  19688     comptime kind: enum { anon_init, typed_init },
  19689     extra_data: switch (kind) {
  19690         .anon_init => Zir.Inst.StructInitAnon,
  19691         .typed_init => Zir.Inst.StructInit,
  19692     },
  19693     extra_end: usize,
  19694     is_ref: bool,
  19695 ) CompileError!Air.Inst.Ref {
  19696     const pt = sema.pt;
  19697     const zcu = pt.zcu;
  19698     const gpa = sema.gpa;
  19699     const ip = &zcu.intern_pool;
  19700     const zir_datas = sema.code.instructions.items(.data);
  19701 
  19702     const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
  19703     const values = try sema.arena.alloc(InternPool.Index, types.len);
  19704     const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len);
  19705 
  19706     var any_values = false;
  19707 
  19708     // Find which field forces the expression to be runtime, if any.
  19709     const opt_runtime_index = rs: {
  19710         var runtime_index: ?usize = null;
  19711         var extra_index = extra_end;
  19712         for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| {
  19713             const item = switch (kind) {
  19714                 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
  19715                 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
  19716             };
  19717             extra_index = item.end;
  19718 
  19719             const name = switch (kind) {
  19720                 .anon_init => sema.code.nullTerminatedString(item.data.field_name),
  19721                 .typed_init => name: {
  19722                     // `item.data.field_type` references a `field_type` instruction
  19723                     const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
  19724                     const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index);
  19725                     break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);
  19726                 },
  19727             };
  19728 
  19729             field_name.* = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
  19730 
  19731             const init = try sema.resolveInst(item.data.init);
  19732             field_ty.* = sema.typeOf(init).toIntern();
  19733             if (Type.fromInterned(field_ty.*).zigTypeTag(zcu) == .@"opaque") {
  19734                 const msg = msg: {
  19735                     const field_src = block.src(.{ .init_elem = .{
  19736                         .init_node_offset = src.offset.node_offset.x,
  19737                         .elem_index = @intCast(i_usize),
  19738                     } });
  19739                     const msg = try sema.errMsg(field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  19740                     errdefer msg.destroy(sema.gpa);
  19741 
  19742                     try sema.addDeclaredHereNote(msg, .fromInterned(field_ty.*));
  19743                     break :msg msg;
  19744                 };
  19745                 return sema.failWithOwnedErrorMsg(block, msg);
  19746             }
  19747             if (try sema.resolveValue(init)) |init_val| {
  19748                 field_val.* = init_val.toIntern();
  19749                 any_values = true;
  19750             } else {
  19751                 field_val.* = .none;
  19752                 runtime_index = @intCast(i_usize);
  19753             }
  19754         }
  19755         break :rs runtime_index;
  19756     };
  19757 
  19758     // We treat anonymous struct types as reified types, because there are similarities:
  19759     // * They use a form of structural equivalence, which we can easily model using a custom hash
  19760     // * They do not have captures
  19761     // * They immediately have their fields resolved
  19762     // In general, other code should treat anon struct types and reified struct types identically,
  19763     // so there's no point having a separate `InternPool.NamespaceType` field for them.
  19764     const type_hash: u64 = hash: {
  19765         var hasher = std.hash.Wyhash.init(0);
  19766         hasher.update(std.mem.sliceAsBytes(types));
  19767         hasher.update(std.mem.sliceAsBytes(values));
  19768         hasher.update(std.mem.sliceAsBytes(names));
  19769         break :hash hasher.final();
  19770     };
  19771     const tracked_inst = try block.trackZir(inst);
  19772     const struct_ty = switch (try ip.getStructType(gpa, pt.tid, .{
  19773         .layout = .auto,
  19774         .fields_len = extra_data.fields_len,
  19775         .known_non_opv = false,
  19776         .requires_comptime = .unknown,
  19777         .any_comptime_fields = any_values,
  19778         .any_default_inits = any_values,
  19779         .inits_resolved = true,
  19780         .any_aligned_fields = false,
  19781         .key = .{ .reified = .{
  19782             .zir_index = tracked_inst,
  19783             .type_hash = type_hash,
  19784         } },
  19785     }, false)) {
  19786         .wip => |wip| ty: {
  19787             errdefer wip.cancel(ip, pt.tid);
  19788             const type_name = try sema.createTypeName(block, .anon, "struct", inst, wip.index);
  19789             wip.setName(ip, type_name.name, type_name.nav);
  19790 
  19791             const struct_type = ip.loadStructType(wip.index);
  19792 
  19793             for (names, values, 0..) |name, init_val, field_idx| {
  19794                 assert(struct_type.addFieldName(ip, name) == null);
  19795                 if (init_val != .none) struct_type.setFieldComptime(ip, field_idx);
  19796             }
  19797 
  19798             @memcpy(struct_type.field_types.get(ip), types);
  19799             if (any_values) {
  19800                 @memcpy(struct_type.field_inits.get(ip), values);
  19801             }
  19802 
  19803             const new_namespace_index = try pt.createNamespace(.{
  19804                 .parent = block.namespace.toOptional(),
  19805                 .owner_type = wip.index,
  19806                 .file_scope = block.getFileScopeIndex(zcu),
  19807                 .generation = zcu.generation,
  19808             });
  19809             try zcu.comp.queueJob(.{ .resolve_type_fully = wip.index });
  19810             codegen_type: {
  19811                 if (zcu.comp.config.use_llvm) break :codegen_type;
  19812                 if (block.ownerModule().strip) break :codegen_type;
  19813                 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
  19814                 try zcu.comp.queueJob(.{ .link_type = wip.index });
  19815             }
  19816             if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
  19817             break :ty wip.finish(ip, new_namespace_index);
  19818         },
  19819         .existing => |ty| ty,
  19820     };
  19821     try sema.declareDependency(.{ .interned = struct_ty });
  19822     try sema.addTypeReferenceEntry(src, struct_ty);
  19823 
  19824     _ = opt_runtime_index orelse {
  19825         const struct_val = try pt.aggregateValue(.fromInterned(struct_ty), values);
  19826         return sema.addConstantMaybeRef(struct_val.toIntern(), is_ref);
  19827     };
  19828 
  19829     if (is_ref) {
  19830         const target = zcu.getTarget();
  19831         const alloc_ty = try pt.ptrTypeSema(.{
  19832             .child = struct_ty,
  19833             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19834         });
  19835         const alloc = try block.addTy(.alloc, alloc_ty);
  19836         var extra_index = extra_end;
  19837         for (types, 0..) |field_ty, i_usize| {
  19838             const i: u32 = @intCast(i_usize);
  19839             const item = switch (kind) {
  19840                 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
  19841                 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
  19842             };
  19843             extra_index = item.end;
  19844 
  19845             const field_ptr_ty = try pt.ptrTypeSema(.{
  19846                 .child = field_ty,
  19847                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19848             });
  19849             if (values[i] == .none) {
  19850                 const init = try sema.resolveInst(item.data.init);
  19851                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  19852                 _ = try block.addBinOp(.store, field_ptr, init);
  19853             }
  19854         }
  19855 
  19856         return sema.makePtrConst(block, alloc);
  19857     }
  19858 
  19859     const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);
  19860     var extra_index = extra_end;
  19861     for (types, 0..) |_, i| {
  19862         const item = switch (kind) {
  19863             .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
  19864             .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
  19865         };
  19866         extra_index = item.end;
  19867         element_refs[i] = try sema.resolveInst(item.data.init);
  19868     }
  19869 
  19870     return block.addAggregateInit(.fromInterned(struct_ty), element_refs);
  19871 }
  19872 
  19873 fn zirArrayInit(
  19874     sema: *Sema,
  19875     block: *Block,
  19876     inst: Zir.Inst.Index,
  19877     is_ref: bool,
  19878 ) CompileError!Air.Inst.Ref {
  19879     const pt = sema.pt;
  19880     const zcu = pt.zcu;
  19881     const gpa = sema.gpa;
  19882     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19883     const src = block.nodeOffset(inst_data.src_node);
  19884 
  19885     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  19886     const args = sema.code.refSlice(extra.end, extra.data.operands_len);
  19887     assert(args.len >= 2); // array_ty + at least one element
  19888 
  19889     const result_ty = try sema.resolveTypeOrPoison(block, src, args[0]) orelse {
  19890         // The type wasn't actually known, so treat this as an anon array init.
  19891         return sema.arrayInitAnon(block, src, args[1..], is_ref);
  19892     };
  19893     const array_ty = result_ty.optEuBaseType(zcu);
  19894     const is_tuple = array_ty.zigTypeTag(zcu) == .@"struct";
  19895     const sentinel_val = array_ty.sentinel(zcu);
  19896 
  19897     var root_msg: ?*Zcu.ErrorMsg = null;
  19898     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  19899 
  19900     const final_len = try sema.usizeCast(block, src, array_ty.arrayLenIncludingSentinel(zcu));
  19901     const resolved_args = try gpa.alloc(Air.Inst.Ref, final_len);
  19902     defer gpa.free(resolved_args);
  19903     for (resolved_args, 0..) |*dest, i| {
  19904         const elem_src = block.src(.{ .init_elem = .{
  19905             .init_node_offset = src.offset.node_offset.x,
  19906             .elem_index = @intCast(i),
  19907         } });
  19908         // Less inits than needed.
  19909         if (i + 2 > args.len) if (is_tuple) {
  19910             const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern();
  19911             if (default_val == .unreachable_value) {
  19912                 const template = "missing tuple field with index {d}";
  19913                 if (root_msg) |msg| {
  19914                     try sema.errNote(src, msg, template, .{i});
  19915                 } else {
  19916                     root_msg = try sema.errMsg(src, template, .{i});
  19917                 }
  19918             } else {
  19919                 dest.* = Air.internedToRef(default_val);
  19920             }
  19921             continue;
  19922         } else {
  19923             dest.* = Air.internedToRef(sentinel_val.?.toIntern());
  19924             break;
  19925         };
  19926 
  19927         const arg = args[i + 1];
  19928         const resolved_arg = try sema.resolveInst(arg);
  19929         const elem_ty = if (is_tuple)
  19930             array_ty.fieldType(i, zcu)
  19931         else
  19932             array_ty.elemType2(zcu);
  19933         dest.* = try sema.coerce(block, elem_ty, resolved_arg, elem_src);
  19934         if (is_tuple) {
  19935             if (array_ty.structFieldIsComptime(i, zcu))
  19936                 try array_ty.resolveStructFieldInits(pt);
  19937             if (try array_ty.structFieldValueComptime(pt, i)) |field_val| {
  19938                 const init_val = try sema.resolveConstValue(block, elem_src, dest.*, .{ .simple = .stored_to_comptime_field });
  19939                 if (!field_val.eql(init_val, elem_ty, zcu)) {
  19940                     return sema.failWithInvalidComptimeFieldStore(block, elem_src, array_ty, i);
  19941                 }
  19942             }
  19943         }
  19944     }
  19945 
  19946     if (root_msg) |msg| {
  19947         try sema.addDeclaredHereNote(msg, array_ty);
  19948         root_msg = null;
  19949         return sema.failWithOwnedErrorMsg(block, msg);
  19950     }
  19951 
  19952     const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| {
  19953         const comptime_known = try sema.isComptimeKnown(arg);
  19954         if (!comptime_known) break @intCast(i);
  19955     } else null;
  19956 
  19957     _ = opt_runtime_index orelse {
  19958         const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len);
  19959         for (elem_vals, resolved_args) |*val, arg| {
  19960             // We checked that all args are comptime above.
  19961             val.* = (sema.resolveValue(arg) catch unreachable).?.toIntern();
  19962         }
  19963         const arr_val = try pt.aggregateValue(array_ty, elem_vals);
  19964         const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val.toIntern()), src);
  19965         const result_val = (try sema.resolveValue(result_ref)).?;
  19966         return sema.addConstantMaybeRef(result_val.toIntern(), is_ref);
  19967     };
  19968 
  19969     if (is_ref) {
  19970         const target = zcu.getTarget();
  19971         const alloc_ty = try pt.ptrTypeSema(.{
  19972             .child = result_ty.toIntern(),
  19973             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19974         });
  19975         const alloc = try block.addTy(.alloc, alloc_ty);
  19976         const base_ptr = try sema.optEuBasePtrInit(block, alloc, src);
  19977 
  19978         if (is_tuple) {
  19979             for (resolved_args, 0..) |arg, i| {
  19980                 const elem_ptr_ty = try pt.ptrTypeSema(.{
  19981                     .child = array_ty.fieldType(i, zcu).toIntern(),
  19982                     .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19983                 });
  19984                 const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern());
  19985 
  19986                 const index = try pt.intRef(.usize, i);
  19987                 const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref);
  19988                 _ = try block.addBinOp(.store, elem_ptr, arg);
  19989             }
  19990             return sema.makePtrConst(block, alloc);
  19991         }
  19992 
  19993         const elem_ptr_ty = try pt.ptrTypeSema(.{
  19994             .child = array_ty.elemType2(zcu).toIntern(),
  19995             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19996         });
  19997         const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern());
  19998 
  19999         for (resolved_args, 0..) |arg, i| {
  20000             const index = try pt.intRef(.usize, i);
  20001             const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref);
  20002             _ = try block.addBinOp(.store, elem_ptr, arg);
  20003         }
  20004         return sema.makePtrConst(block, alloc);
  20005     }
  20006 
  20007     const arr_ref = try block.addAggregateInit(array_ty, resolved_args);
  20008     return sema.coerce(block, result_ty, arr_ref, src);
  20009 }
  20010 
  20011 fn zirArrayInitAnon(
  20012     sema: *Sema,
  20013     block: *Block,
  20014     inst: Zir.Inst.Index,
  20015 ) CompileError!Air.Inst.Ref {
  20016     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  20017     const src = block.nodeOffset(inst_data.src_node);
  20018     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  20019     const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
  20020     return sema.arrayInitAnon(block, src, operands, false);
  20021 }
  20022 
  20023 fn arrayInitAnon(
  20024     sema: *Sema,
  20025     block: *Block,
  20026     src: LazySrcLoc,
  20027     operands: []const Zir.Inst.Ref,
  20028     is_ref: bool,
  20029 ) CompileError!Air.Inst.Ref {
  20030     const pt = sema.pt;
  20031     const zcu = pt.zcu;
  20032     const gpa = sema.gpa;
  20033     const ip = &zcu.intern_pool;
  20034 
  20035     const types = try sema.arena.alloc(InternPool.Index, operands.len);
  20036     const values = try sema.arena.alloc(InternPool.Index, operands.len);
  20037 
  20038     const opt_runtime_src = rs: {
  20039         var runtime_src: ?LazySrcLoc = null;
  20040         for (operands, 0..) |operand, i| {
  20041             const operand_src = src; // TODO better source location
  20042             const elem = try sema.resolveInst(operand);
  20043             types[i] = sema.typeOf(elem).toIntern();
  20044             if (Type.fromInterned(types[i]).zigTypeTag(zcu) == .@"opaque") {
  20045                 const msg = msg: {
  20046                     const msg = try sema.errMsg(operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  20047                     errdefer msg.destroy(gpa);
  20048 
  20049                     try sema.addDeclaredHereNote(msg, .fromInterned(types[i]));
  20050                     break :msg msg;
  20051                 };
  20052                 return sema.failWithOwnedErrorMsg(block, msg);
  20053             }
  20054             if (try sema.resolveValue(elem)) |val| {
  20055                 values[i] = val.toIntern();
  20056             } else {
  20057                 values[i] = .none;
  20058                 runtime_src = operand_src;
  20059             }
  20060         }
  20061         break :rs runtime_src;
  20062     };
  20063 
  20064     const tuple_ty: Type = .fromInterned(try ip.getTupleType(gpa, pt.tid, .{
  20065         .types = types,
  20066         .values = values,
  20067     }));
  20068 
  20069     const runtime_src = opt_runtime_src orelse {
  20070         const tuple_val = try pt.aggregateValue(tuple_ty, values);
  20071         return sema.addConstantMaybeRef(tuple_val.toIntern(), is_ref);
  20072     };
  20073 
  20074     try sema.requireRuntimeBlock(block, src, runtime_src);
  20075 
  20076     if (is_ref) {
  20077         const target = sema.pt.zcu.getTarget();
  20078         const alloc_ty = try pt.ptrTypeSema(.{
  20079             .child = tuple_ty.toIntern(),
  20080             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  20081         });
  20082         const alloc = try block.addTy(.alloc, alloc_ty);
  20083         for (operands, 0..) |operand, i_usize| {
  20084             const i: u32 = @intCast(i_usize);
  20085             const field_ptr_ty = try pt.ptrTypeSema(.{
  20086                 .child = types[i],
  20087                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  20088             });
  20089             if (values[i] == .none) {
  20090                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  20091                 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand));
  20092             }
  20093         }
  20094 
  20095         return sema.makePtrConst(block, alloc);
  20096     }
  20097 
  20098     const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  20099     for (operands, 0..) |operand, i| {
  20100         element_refs[i] = try sema.resolveInst(operand);
  20101     }
  20102 
  20103     return block.addAggregateInit(tuple_ty, element_refs);
  20104 }
  20105 
  20106 fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.Inst.Ref {
  20107     return if (is_ref) sema.uavRef(val) else Air.internedToRef(val);
  20108 }
  20109 
  20110 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20111     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  20112     const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
  20113     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20114     const field_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  20115     const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
  20116     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name });
  20117     return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
  20118 }
  20119 
  20120 fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20121     const pt = sema.pt;
  20122     const zcu = pt.zcu;
  20123     const ip = &zcu.intern_pool;
  20124     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  20125     const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
  20126     const ty_src = block.nodeOffset(inst_data.src_node);
  20127     const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node });
  20128     const wrapped_aggregate_ty = try sema.resolveTypeOrPoison(block, ty_src, extra.container_type) orelse return .generic_poison_type;
  20129     const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu);
  20130     const zir_field_name = sema.code.nullTerminatedString(extra.name_start);
  20131     const field_name = try ip.getOrPutString(sema.gpa, pt.tid, zir_field_name, .no_embedded_nulls);
  20132     return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
  20133 }
  20134 
  20135 fn fieldType(
  20136     sema: *Sema,
  20137     block: *Block,
  20138     aggregate_ty: Type,
  20139     field_name: InternPool.NullTerminatedString,
  20140     field_src: LazySrcLoc,
  20141     ty_src: LazySrcLoc,
  20142 ) CompileError!Air.Inst.Ref {
  20143     const pt = sema.pt;
  20144     const zcu = pt.zcu;
  20145     const ip = &zcu.intern_pool;
  20146     var cur_ty = aggregate_ty;
  20147     while (true) {
  20148         try cur_ty.resolveFields(pt);
  20149         switch (cur_ty.zigTypeTag(zcu)) {
  20150             .@"struct" => switch (ip.indexToKey(cur_ty.toIntern())) {
  20151                 .tuple_type => |tuple| {
  20152                     const field_index = try sema.tupleFieldIndex(block, cur_ty, field_name, field_src);
  20153                     return Air.internedToRef(tuple.types.get(ip)[field_index]);
  20154                 },
  20155                 .struct_type => {
  20156                     const struct_type = ip.loadStructType(cur_ty.toIntern());
  20157                     const field_index = struct_type.nameIndex(ip, field_name) orelse
  20158                         return sema.failWithBadStructFieldAccess(block, cur_ty, struct_type, field_src, field_name);
  20159                     const field_ty = struct_type.field_types.get(ip)[field_index];
  20160                     return Air.internedToRef(field_ty);
  20161                 },
  20162                 else => unreachable,
  20163             },
  20164             .@"union" => {
  20165                 const union_obj = zcu.typeToUnion(cur_ty).?;
  20166                 const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse
  20167                     return sema.failWithBadUnionFieldAccess(block, cur_ty, union_obj, field_src, field_name);
  20168                 const field_ty = union_obj.field_types.get(ip)[field_index];
  20169                 return Air.internedToRef(field_ty);
  20170             },
  20171             .optional => {
  20172                 // Struct/array init through optional requires the child type to not be a pointer.
  20173                 // If the child of .optional is a pointer it'll error on the next loop.
  20174                 cur_ty = .fromInterned(ip.indexToKey(cur_ty.toIntern()).opt_type);
  20175                 continue;
  20176             },
  20177             .error_union => {
  20178                 cur_ty = cur_ty.errorUnionPayload(zcu);
  20179                 continue;
  20180             },
  20181             else => {},
  20182         }
  20183         return sema.fail(block, ty_src, "expected struct or union; found '{f}'", .{
  20184             cur_ty.fmt(pt),
  20185         });
  20186     }
  20187 }
  20188 
  20189 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  20190     return sema.getErrorReturnTrace(block);
  20191 }
  20192 
  20193 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  20194     const pt = sema.pt;
  20195     const zcu = pt.zcu;
  20196     const ip = &zcu.intern_pool;
  20197     const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
  20198     try stack_trace_ty.resolveFields(pt);
  20199     const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
  20200     const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern());
  20201 
  20202     switch (sema.owner.unwrap()) {
  20203         .func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) {
  20204             return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
  20205         },
  20206         .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
  20207     }
  20208     return Air.internedToRef(try pt.intern(.{ .opt = .{
  20209         .ty = opt_ptr_stack_trace_ty.toIntern(),
  20210         .val = .none,
  20211     } }));
  20212 }
  20213 
  20214 fn zirFrame(
  20215     sema: *Sema,
  20216     block: *Block,
  20217     extended: Zir.Inst.Extended.InstData,
  20218 ) CompileError!Air.Inst.Ref {
  20219     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  20220     const src = block.nodeOffset(src_node);
  20221     return sema.failWithUseOfAsync(block, src);
  20222 }
  20223 
  20224 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20225     const zcu = sema.pt.zcu;
  20226     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20227     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20228     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
  20229     if (ty.isNoReturn(zcu)) {
  20230         return sema.fail(block, operand_src, "no align available for type '{f}'", .{ty.fmt(sema.pt)});
  20231     }
  20232     const val = try ty.lazyAbiAlignment(sema.pt);
  20233     return Air.internedToRef(val.toIntern());
  20234 }
  20235 
  20236 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20237     const pt = sema.pt;
  20238     const zcu = pt.zcu;
  20239     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20240     const src = block.nodeOffset(inst_data.src_node);
  20241     const operand = try sema.resolveInst(inst_data.operand);
  20242     const operand_ty = sema.typeOf(operand);
  20243     const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
  20244     const operand_scalar_ty = operand_ty.scalarType(zcu);
  20245     if (operand_scalar_ty.toIntern() != .bool_type) {
  20246         return sema.fail(block, src, "expected 'bool', found '{t}'", .{operand_scalar_ty.zigTypeTag(zcu)});
  20247     }
  20248     const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;
  20249     const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1;
  20250     if (try sema.resolveValue(operand)) |val| {
  20251         if (!is_vector) {
  20252             return if (val.isUndef(zcu)) .undef_u1 else if (val.toBool()) .one_u1 else .zero_u1;
  20253         }
  20254         if (val.isUndef(zcu)) return pt.undefRef(dest_ty);
  20255         const new_elems = try sema.arena.alloc(InternPool.Index, len);
  20256         for (new_elems, 0..) |*new_elem, i| {
  20257             const old_elem = try val.elemValue(pt, i);
  20258             new_elem.* = if (old_elem.isUndef(zcu))
  20259                 .undef_u1
  20260             else if (old_elem.toBool())
  20261                 .one_u1
  20262             else
  20263                 .zero_u1;
  20264         }
  20265         return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern());
  20266     }
  20267     return block.addBitCast(dest_ty, operand);
  20268 }
  20269 
  20270 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20271     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20272     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20273     const uncoerced_operand = try sema.resolveInst(inst_data.operand);
  20274     const operand = try sema.coerce(block, .anyerror, uncoerced_operand, operand_src);
  20275 
  20276     if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
  20277         const err_name = sema.pt.zcu.intern_pool.indexToKey(val.toIntern()).err.name;
  20278         return sema.addNullTerminatedStrLit(err_name);
  20279     }
  20280 
  20281     // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass
  20282     // might be able to resolve the result at compile time.
  20283     return block.addUnOp(.error_name, operand);
  20284 }
  20285 
  20286 fn zirAbs(
  20287     sema: *Sema,
  20288     block: *Block,
  20289     inst: Zir.Inst.Index,
  20290 ) CompileError!Air.Inst.Ref {
  20291     const pt = sema.pt;
  20292     const zcu = pt.zcu;
  20293     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20294     const operand = try sema.resolveInst(inst_data.operand);
  20295     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20296     const operand_ty = sema.typeOf(operand);
  20297     const scalar_ty = operand_ty.scalarType(zcu);
  20298 
  20299     const result_ty = switch (scalar_ty.zigTypeTag(zcu)) {
  20300         .comptime_float, .float, .comptime_int => operand_ty,
  20301         .int => if (scalar_ty.isSignedInt(zcu)) try operand_ty.toUnsigned(pt) else return operand,
  20302         else => return sema.fail(
  20303             block,
  20304             operand_src,
  20305             "expected integer, float, or vector of either integers or floats, found '{f}'",
  20306             .{operand_ty.fmt(pt)},
  20307         ),
  20308     };
  20309 
  20310     return (try sema.maybeConstantUnaryMath(operand, result_ty, Value.abs)) orelse {
  20311         try sema.requireRuntimeBlock(block, operand_src, null);
  20312         return block.addTyOp(.abs, result_ty, operand);
  20313     };
  20314 }
  20315 
  20316 fn maybeConstantUnaryMath(
  20317     sema: *Sema,
  20318     operand: Air.Inst.Ref,
  20319     result_ty: Type,
  20320     comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value,
  20321 ) CompileError!?Air.Inst.Ref {
  20322     const pt = sema.pt;
  20323     const zcu = pt.zcu;
  20324     switch (result_ty.zigTypeTag(zcu)) {
  20325         .vector => if (try sema.resolveValue(operand)) |val| {
  20326             const scalar_ty = result_ty.scalarType(zcu);
  20327             const vec_len = result_ty.vectorLen(zcu);
  20328             if (val.isUndef(zcu))
  20329                 return try pt.undefRef(result_ty);
  20330 
  20331             const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  20332             for (elems, 0..) |*elem, i| {
  20333                 const elem_val = try val.elemValue(pt, i);
  20334                 elem.* = (try eval(elem_val, scalar_ty, sema.arena, pt)).toIntern();
  20335             }
  20336             return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern());
  20337         },
  20338         else => if (try sema.resolveValue(operand)) |operand_val| {
  20339             if (operand_val.isUndef(zcu))
  20340                 return try pt.undefRef(result_ty);
  20341             const result_val = try eval(operand_val, result_ty, sema.arena, pt);
  20342             return Air.internedToRef(result_val.toIntern());
  20343         },
  20344     }
  20345     return null;
  20346 }
  20347 
  20348 fn zirUnaryMath(
  20349     sema: *Sema,
  20350     block: *Block,
  20351     inst: Zir.Inst.Index,
  20352     air_tag: Air.Inst.Tag,
  20353     comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value,
  20354 ) CompileError!Air.Inst.Ref {
  20355     const tracy = trace(@src());
  20356     defer tracy.end();
  20357 
  20358     const pt = sema.pt;
  20359     const zcu = pt.zcu;
  20360     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20361     const operand = try sema.resolveInst(inst_data.operand);
  20362     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20363     const operand_ty = sema.typeOf(operand);
  20364     const scalar_ty = operand_ty.scalarType(zcu);
  20365 
  20366     switch (scalar_ty.zigTypeTag(zcu)) {
  20367         .comptime_float, .float => {},
  20368         else => return sema.fail(
  20369             block,
  20370             operand_src,
  20371             "expected vector of floats or float type, found '{f}'",
  20372             .{operand_ty.fmt(pt)},
  20373         ),
  20374     }
  20375 
  20376     return (try sema.maybeConstantUnaryMath(operand, operand_ty, eval)) orelse {
  20377         try sema.requireRuntimeBlock(block, operand_src, null);
  20378         return block.addUnOp(air_tag, operand);
  20379     };
  20380 }
  20381 
  20382 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20383     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20384     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20385     const src = block.nodeOffset(inst_data.src_node);
  20386     const operand = try sema.resolveInst(inst_data.operand);
  20387     const operand_ty = sema.typeOf(operand);
  20388     const pt = sema.pt;
  20389     const zcu = pt.zcu;
  20390     const ip = &zcu.intern_pool;
  20391     try operand_ty.resolveLayout(pt);
  20392     const enum_ty = switch (operand_ty.zigTypeTag(zcu)) {
  20393         .enum_literal => {
  20394             const val = (try sema.resolveDefinedValue(block, operand_src, operand)).?;
  20395             const tag_name = ip.indexToKey(val.toIntern()).enum_literal;
  20396             return sema.addNullTerminatedStrLit(tag_name);
  20397         },
  20398         .@"enum" => operand_ty,
  20399         .@"union" => operand_ty.unionTagType(zcu) orelse
  20400             return sema.fail(block, src, "union '{f}' is untagged", .{operand_ty.fmt(pt)}),
  20401         else => return sema.fail(block, operand_src, "expected enum or union; found '{f}'", .{
  20402             operand_ty.fmt(pt),
  20403         }),
  20404     };
  20405     if (enum_ty.enumFieldCount(zcu) == 0) {
  20406         // TODO I don't think this is the correct way to handle this but
  20407         // it prevents a crash.
  20408         // https://github.com/ziglang/zig/issues/15909
  20409         return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{f}'", .{
  20410             enum_ty.fmt(pt),
  20411         });
  20412     }
  20413     const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
  20414     if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
  20415         const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse {
  20416             const msg = msg: {
  20417                 const msg = try sema.errMsg(src, "no field with value '{f}' in enum '{f}'", .{
  20418                     val.fmtValueSema(pt, sema), enum_ty.fmt(pt),
  20419                 });
  20420                 errdefer msg.destroy(sema.gpa);
  20421                 try sema.errNote(enum_ty.srcLoc(zcu), msg, "declared here", .{});
  20422                 break :msg msg;
  20423             };
  20424             return sema.failWithOwnedErrorMsg(block, msg);
  20425         };
  20426         // TODO: write something like getCoercedInts to avoid needing to dupe
  20427         const field_name = enum_ty.enumFieldName(field_index, zcu);
  20428         return sema.addNullTerminatedStrLit(field_name);
  20429     }
  20430     try sema.requireRuntimeBlock(block, src, operand_src);
  20431     if (block.wantSafety() and zcu.backendSupportsFeature(.is_named_enum_value)) {
  20432         const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
  20433         try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
  20434     }
  20435     // In case the value is runtime-known, we have an AIR instruction for this instead
  20436     // of trying to lower it in Sema because an optimization pass may result in the operand
  20437     // being comptime-known, which would let us elide the `tag_name` AIR instruction.
  20438     return block.addUnOp(.tag_name, casted_operand);
  20439 }
  20440 
  20441 fn zirReify(
  20442     sema: *Sema,
  20443     block: *Block,
  20444     extended: Zir.Inst.Extended.InstData,
  20445     inst: Zir.Inst.Index,
  20446 ) CompileError!Air.Inst.Ref {
  20447     const pt = sema.pt;
  20448     const zcu = pt.zcu;
  20449     const gpa = sema.gpa;
  20450     const ip = &zcu.intern_pool;
  20451     const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small);
  20452     const extra = sema.code.extraData(Zir.Inst.Reify, extended.operand).data;
  20453     const tracked_inst = try block.trackZir(inst);
  20454     const src: LazySrcLoc = .{
  20455         .base_node_inst = tracked_inst,
  20456         .offset = LazySrcLoc.Offset.nodeOffset(.zero),
  20457     };
  20458     const operand_src: LazySrcLoc = .{
  20459         .base_node_inst = tracked_inst,
  20460         .offset = .{
  20461             .node_offset_builtin_call_arg = .{
  20462                 .builtin_call_node = .zero, // `tracked_inst` is precisely the `reify` instruction, so offset is 0
  20463                 .arg_index = 0,
  20464             },
  20465         },
  20466     };
  20467     const type_info_ty = try sema.getBuiltinType(src, .Type);
  20468     const uncasted_operand = try sema.resolveInst(extra.operand);
  20469     const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
  20470     const val = try sema.resolveConstDefinedValue(block, operand_src, type_info, .{ .simple = .operand_Type });
  20471     const union_val = ip.indexToKey(val.toIntern()).un;
  20472     if (try sema.anyUndef(block, operand_src, Value.fromInterned(union_val.val))) {
  20473         return sema.failWithUseOfUndef(block, operand_src, null);
  20474     }
  20475     const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), zcu).?;
  20476     switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) {
  20477         .type => return .type_type,
  20478         .void => return .void_type,
  20479         .bool => return .bool_type,
  20480         .noreturn => return .noreturn_type,
  20481         .comptime_float => return .comptime_float_type,
  20482         .comptime_int => return .comptime_int_type,
  20483         .undefined => return .undefined_type,
  20484         .null => return .null_type,
  20485         .@"anyframe" => return sema.failWithUseOfAsync(block, src),
  20486         .enum_literal => return .enum_literal_type,
  20487         .int => {
  20488             const int = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Int);
  20489             const ty = try pt.intType(int.signedness, int.bits);
  20490             return Air.internedToRef(ty.toIntern());
  20491         },
  20492         .vector => {
  20493             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20494             const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20495                 ip,
  20496                 try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls),
  20497             ).?);
  20498             const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20499                 ip,
  20500                 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls),
  20501             ).?);
  20502 
  20503             const len: u32 = @intCast(try len_val.toUnsignedIntSema(pt));
  20504             const child_ty = child_val.toType();
  20505 
  20506             try sema.checkVectorElemType(block, src, child_ty);
  20507 
  20508             const ty = try pt.vectorType(.{
  20509                 .len = len,
  20510                 .child = child_ty.toIntern(),
  20511             });
  20512             return Air.internedToRef(ty.toIntern());
  20513         },
  20514         .float => {
  20515             const float = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Float);
  20516 
  20517             const ty: Type = switch (float.bits) {
  20518                 16 => .f16,
  20519                 32 => .f32,
  20520                 64 => .f64,
  20521                 80 => .f80,
  20522                 128 => .f128,
  20523                 else => return sema.fail(block, src, "{d}-bit float unsupported", .{float.bits}),
  20524             };
  20525             return Air.internedToRef(ty.toIntern());
  20526         },
  20527         .pointer => {
  20528             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20529             const size_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20530                 ip,
  20531                 try ip.getOrPutString(gpa, pt.tid, "size", .no_embedded_nulls),
  20532             ).?);
  20533             const is_const_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20534                 ip,
  20535                 try ip.getOrPutString(gpa, pt.tid, "is_const", .no_embedded_nulls),
  20536             ).?);
  20537             const is_volatile_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20538                 ip,
  20539                 try ip.getOrPutString(gpa, pt.tid, "is_volatile", .no_embedded_nulls),
  20540             ).?);
  20541             const alignment_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20542                 ip,
  20543                 try ip.getOrPutString(gpa, pt.tid, "alignment", .no_embedded_nulls),
  20544             ).?);
  20545             const address_space_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20546                 ip,
  20547                 try ip.getOrPutString(gpa, pt.tid, "address_space", .no_embedded_nulls),
  20548             ).?);
  20549             const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20550                 ip,
  20551                 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls),
  20552             ).?);
  20553             const is_allowzero_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20554                 ip,
  20555                 try ip.getOrPutString(gpa, pt.tid, "is_allowzero", .no_embedded_nulls),
  20556             ).?);
  20557             const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20558                 ip,
  20559                 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls),
  20560             ).?);
  20561 
  20562             if (!try sema.intFitsInType(alignment_val, align_ty, null)) {
  20563                 return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)});
  20564             }
  20565             const alignment_val_int = try alignment_val.toUnsignedIntSema(pt);
  20566             const abi_align = try sema.validateAlign(block, src, alignment_val_int);
  20567 
  20568             const elem_ty = child_val.toType();
  20569             if (abi_align != .none) {
  20570                 try elem_ty.resolveLayout(pt);
  20571             }
  20572 
  20573             const ptr_size = try sema.interpretBuiltinType(block, operand_src, size_val, std.builtin.Type.Pointer.Size);
  20574 
  20575             const actual_sentinel: InternPool.Index = s: {
  20576                 if (!sentinel_val.isNull(zcu)) {
  20577                     if (ptr_size == .one or ptr_size == .c) {
  20578                         return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{});
  20579                     }
  20580                     const sentinel_ptr_val = sentinel_val.optionalValue(zcu).?;
  20581                     const ptr_ty = try pt.singleMutPtrType(elem_ty);
  20582                     const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
  20583                     try sema.checkSentinelType(block, src, elem_ty);
  20584                     break :s sent_val.toIntern();
  20585                 }
  20586                 break :s .none;
  20587             };
  20588 
  20589             if (elem_ty.zigTypeTag(zcu) == .noreturn) {
  20590                 return sema.fail(block, src, "pointer to noreturn not allowed", .{});
  20591             } else if (elem_ty.zigTypeTag(zcu) == .@"fn") {
  20592                 if (ptr_size != .one) {
  20593                     return sema.fail(block, src, "function pointers must be single pointers", .{});
  20594                 }
  20595             } else if (ptr_size == .many and elem_ty.zigTypeTag(zcu) == .@"opaque") {
  20596                 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
  20597             } else if (ptr_size == .c) {
  20598                 if (!try sema.validateExternType(elem_ty, .other)) {
  20599                     const msg = msg: {
  20600                         const msg = try sema.errMsg(src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)});
  20601                         errdefer msg.destroy(gpa);
  20602 
  20603                         try sema.explainWhyTypeIsNotExtern(msg, src, elem_ty, .other);
  20604 
  20605                         try sema.addDeclaredHereNote(msg, elem_ty);
  20606                         break :msg msg;
  20607                     };
  20608                     return sema.failWithOwnedErrorMsg(block, msg);
  20609                 }
  20610                 if (elem_ty.zigTypeTag(zcu) == .@"opaque") {
  20611                     return sema.fail(block, src, "C pointers cannot point to opaque types", .{});
  20612                 }
  20613             }
  20614 
  20615             const ty = try pt.ptrTypeSema(.{
  20616                 .child = elem_ty.toIntern(),
  20617                 .sentinel = actual_sentinel,
  20618                 .flags = .{
  20619                     .size = ptr_size,
  20620                     .is_const = is_const_val.toBool(),
  20621                     .is_volatile = is_volatile_val.toBool(),
  20622                     .alignment = abi_align,
  20623                     .address_space = try sema.interpretBuiltinType(block, operand_src, address_space_val, std.builtin.AddressSpace),
  20624                     .is_allowzero = is_allowzero_val.toBool(),
  20625                 },
  20626             });
  20627             return Air.internedToRef(ty.toIntern());
  20628         },
  20629         .array => {
  20630             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20631             const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20632                 ip,
  20633                 try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls),
  20634             ).?);
  20635             const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20636                 ip,
  20637                 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls),
  20638             ).?);
  20639             const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20640                 ip,
  20641                 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls),
  20642             ).?);
  20643 
  20644             const len = try len_val.toUnsignedIntSema(pt);
  20645             const child_ty = child_val.toType();
  20646             const sentinel = if (sentinel_val.optionalValue(zcu)) |p| blk: {
  20647                 const ptr_ty = try pt.singleMutPtrType(child_ty);
  20648                 try sema.checkSentinelType(block, src, child_ty);
  20649                 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?;
  20650             } else null;
  20651 
  20652             const ty = try pt.arrayType(.{
  20653                 .len = len,
  20654                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  20655                 .child = child_ty.toIntern(),
  20656             });
  20657             return Air.internedToRef(ty.toIntern());
  20658         },
  20659         .optional => {
  20660             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20661             const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20662                 ip,
  20663                 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls),
  20664             ).?);
  20665 
  20666             const child_ty = child_val.toType();
  20667 
  20668             const ty = try pt.optionalType(child_ty.toIntern());
  20669             return Air.internedToRef(ty.toIntern());
  20670         },
  20671         .error_union => {
  20672             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20673             const error_set_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20674                 ip,
  20675                 try ip.getOrPutString(gpa, pt.tid, "error_set", .no_embedded_nulls),
  20676             ).?);
  20677             const payload_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20678                 ip,
  20679                 try ip.getOrPutString(gpa, pt.tid, "payload", .no_embedded_nulls),
  20680             ).?);
  20681 
  20682             const error_set_ty = error_set_val.toType();
  20683             const payload_ty = payload_val.toType();
  20684 
  20685             if (error_set_ty.zigTypeTag(zcu) != .error_set) {
  20686                 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{});
  20687             }
  20688 
  20689             const ty = try pt.errorUnionType(error_set_ty, payload_ty);
  20690             return Air.internedToRef(ty.toIntern());
  20691         },
  20692         .error_set => {
  20693             const payload_val = Value.fromInterned(union_val.val).optionalValue(zcu) orelse
  20694                 return .anyerror_type;
  20695 
  20696             const names_val = try sema.derefSliceAsArray(block, src, payload_val, .{ .simple = .error_set_contents });
  20697 
  20698             const len = try sema.usizeCast(block, src, names_val.typeOf(zcu).arrayLen(zcu));
  20699             var names: InferredErrorSet.NameMap = .{};
  20700             try names.ensureUnusedCapacity(sema.arena, len);
  20701             for (0..len) |i| {
  20702                 const elem_val = try names_val.elemValue(pt, i);
  20703                 const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern()));
  20704                 const name_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex(
  20705                     ip,
  20706                     try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls),
  20707                 ).?);
  20708 
  20709                 const name = try sema.sliceToIpString(block, src, name_val, .{ .simple = .error_set_contents });
  20710                 _ = try pt.getErrorValue(name);
  20711                 const gop = names.getOrPutAssumeCapacity(name);
  20712                 if (gop.found_existing) {
  20713                     return sema.fail(block, src, "duplicate error '{f}'", .{
  20714                         name.fmt(ip),
  20715                     });
  20716                 }
  20717             }
  20718 
  20719             const ty = try pt.errorSetFromUnsortedNames(names.keys());
  20720             return Air.internedToRef(ty.toIntern());
  20721         },
  20722         .@"struct" => {
  20723             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20724             const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20725                 ip,
  20726                 try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls),
  20727             ).?);
  20728             const backing_integer_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20729                 ip,
  20730                 try ip.getOrPutString(gpa, pt.tid, "backing_integer", .no_embedded_nulls),
  20731             ).?);
  20732             const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20733                 ip,
  20734                 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls),
  20735             ).?);
  20736             const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20737                 ip,
  20738                 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls),
  20739             ).?);
  20740             const is_tuple_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20741                 ip,
  20742                 try ip.getOrPutString(gpa, pt.tid, "is_tuple", .no_embedded_nulls),
  20743             ).?);
  20744 
  20745             const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout);
  20746 
  20747             // Decls
  20748             if (try decls_val.sliceLen(pt) > 0) {
  20749                 return sema.fail(block, src, "reified structs must have no decls", .{});
  20750             }
  20751 
  20752             if (layout != .@"packed" and !backing_integer_val.isNull(zcu)) {
  20753                 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});
  20754             }
  20755 
  20756             const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .struct_fields });
  20757 
  20758             if (is_tuple_val.toBool()) {
  20759                 switch (layout) {
  20760                     .@"extern" => return sema.fail(block, src, "extern tuples are not supported", .{}),
  20761                     .@"packed" => return sema.fail(block, src, "packed tuples are not supported", .{}),
  20762                     .auto => {},
  20763                 }
  20764                 return sema.reifyTuple(block, src, fields_arr);
  20765             } else {
  20766                 return sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_arr, name_strategy);
  20767             }
  20768         },
  20769         .@"enum" => {
  20770             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20771             const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20772                 ip,
  20773                 try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls),
  20774             ).?);
  20775             const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20776                 ip,
  20777                 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls),
  20778             ).?);
  20779             const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20780                 ip,
  20781                 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls),
  20782             ).?);
  20783             const is_exhaustive_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20784                 ip,
  20785                 try ip.getOrPutString(gpa, pt.tid, "is_exhaustive", .no_embedded_nulls),
  20786             ).?);
  20787 
  20788             if (try decls_val.sliceLen(pt) > 0) {
  20789                 return sema.fail(block, src, "reified enums must have no decls", .{});
  20790             }
  20791 
  20792             const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .enum_fields });
  20793 
  20794             return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_arr, name_strategy);
  20795         },
  20796         .@"opaque" => {
  20797             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20798             const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20799                 ip,
  20800                 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls),
  20801             ).?);
  20802 
  20803             // Decls
  20804             if (try decls_val.sliceLen(pt) > 0) {
  20805                 return sema.fail(block, src, "reified opaque must have no decls", .{});
  20806             }
  20807 
  20808             const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, .{
  20809                 .key = .{ .reified = .{
  20810                     .zir_index = try block.trackZir(inst),
  20811                 } },
  20812             })) {
  20813                 .existing => |ty| {
  20814                     try sema.addTypeReferenceEntry(src, ty);
  20815                     return Air.internedToRef(ty);
  20816                 },
  20817                 .wip => |wip| wip,
  20818             };
  20819             errdefer wip_ty.cancel(ip, pt.tid);
  20820 
  20821             const type_name = try sema.createTypeName(
  20822                 block,
  20823                 name_strategy,
  20824                 "opaque",
  20825                 inst,
  20826                 wip_ty.index,
  20827             );
  20828             wip_ty.setName(ip, type_name.name, type_name.nav);
  20829 
  20830             const new_namespace_index = try pt.createNamespace(.{
  20831                 .parent = block.namespace.toOptional(),
  20832                 .owner_type = wip_ty.index,
  20833                 .file_scope = block.getFileScopeIndex(zcu),
  20834                 .generation = zcu.generation,
  20835             });
  20836 
  20837             try sema.addTypeReferenceEntry(src, wip_ty.index);
  20838             if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
  20839             return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
  20840         },
  20841         .@"union" => {
  20842             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20843             const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20844                 ip,
  20845                 try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls),
  20846             ).?);
  20847             const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20848                 ip,
  20849                 try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls),
  20850             ).?);
  20851             const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20852                 ip,
  20853                 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls),
  20854             ).?);
  20855             const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20856                 ip,
  20857                 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls),
  20858             ).?);
  20859 
  20860             if (try decls_val.sliceLen(pt) > 0) {
  20861                 return sema.fail(block, src, "reified unions must have no decls", .{});
  20862             }
  20863             const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout);
  20864 
  20865             const has_tag = tag_type_val.optionalValue(zcu) != null;
  20866 
  20867             if (has_tag) {
  20868                 switch (layout) {
  20869                     .@"extern" => return sema.fail(block, src, "extern union does not support enum tag type", .{}),
  20870                     .@"packed" => return sema.fail(block, src, "packed union does not support enum tag type", .{}),
  20871                     .auto => {},
  20872                 }
  20873             }
  20874 
  20875             const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .union_fields });
  20876 
  20877             return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_arr, name_strategy);
  20878         },
  20879         .@"fn" => {
  20880             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20881             const calling_convention_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20882                 ip,
  20883                 try ip.getOrPutString(gpa, pt.tid, "calling_convention", .no_embedded_nulls),
  20884             ).?);
  20885             const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20886                 ip,
  20887                 try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls),
  20888             ).?);
  20889             const is_var_args_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20890                 ip,
  20891                 try ip.getOrPutString(gpa, pt.tid, "is_var_args", .no_embedded_nulls),
  20892             ).?);
  20893             const return_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20894                 ip,
  20895                 try ip.getOrPutString(gpa, pt.tid, "return_type", .no_embedded_nulls),
  20896             ).?);
  20897             const params_slice_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20898                 ip,
  20899                 try ip.getOrPutString(gpa, pt.tid, "params", .no_embedded_nulls),
  20900             ).?);
  20901 
  20902             const is_generic = is_generic_val.toBool();
  20903             if (is_generic) {
  20904                 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{});
  20905             }
  20906 
  20907             const is_var_args = is_var_args_val.toBool();
  20908             const cc = try sema.analyzeValueAsCallconv(block, src, calling_convention_val);
  20909             if (is_var_args) {
  20910                 try sema.checkCallConvSupportsVarArgs(block, src, cc);
  20911             }
  20912 
  20913             const return_type = return_type_val.optionalValue(zcu) orelse
  20914                 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{});
  20915 
  20916             const params_val = try sema.derefSliceAsArray(block, operand_src, params_slice_val, .{ .simple = .function_parameters });
  20917 
  20918             const args_len = try sema.usizeCast(block, src, params_val.typeOf(zcu).arrayLen(zcu));
  20919             const param_types = try sema.arena.alloc(InternPool.Index, args_len);
  20920 
  20921             var noalias_bits: u32 = 0;
  20922             for (param_types, 0..) |*param_type, i| {
  20923                 const elem_val = try params_val.elemValue(pt, i);
  20924                 const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern()));
  20925                 const param_is_generic_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex(
  20926                     ip,
  20927                     try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls),
  20928                 ).?);
  20929                 const param_is_noalias_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex(
  20930                     ip,
  20931                     try ip.getOrPutString(gpa, pt.tid, "is_noalias", .no_embedded_nulls),
  20932                 ).?);
  20933                 const opt_param_type_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex(
  20934                     ip,
  20935                     try ip.getOrPutString(gpa, pt.tid, "type", .no_embedded_nulls),
  20936                 ).?);
  20937 
  20938                 if (param_is_generic_val.toBool()) {
  20939                     return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{});
  20940                 }
  20941 
  20942                 const param_type_val = opt_param_type_val.optionalValue(zcu) orelse
  20943                     return sema.fail(block, src, "Type.Fn.Param.type must be non-null for @Type", .{});
  20944                 param_type.* = param_type_val.toIntern();
  20945 
  20946                 if (param_is_noalias_val.toBool()) {
  20947                     if (!Type.fromInterned(param_type.*).isPtrAtRuntime(zcu)) {
  20948                         return sema.fail(block, src, "non-pointer parameter declared noalias", .{});
  20949                     }
  20950                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse
  20951                         return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
  20952                 }
  20953             }
  20954 
  20955             const ty = try pt.funcType(.{
  20956                 .param_types = param_types,
  20957                 .noalias_bits = noalias_bits,
  20958                 .return_type = return_type.toIntern(),
  20959                 .cc = cc,
  20960                 .is_var_args = is_var_args,
  20961             });
  20962             return Air.internedToRef(ty.toIntern());
  20963         },
  20964         .frame => return sema.failWithUseOfAsync(block, src),
  20965     }
  20966 }
  20967 
  20968 fn reifyEnum(
  20969     sema: *Sema,
  20970     block: *Block,
  20971     inst: Zir.Inst.Index,
  20972     src: LazySrcLoc,
  20973     tag_ty: Type,
  20974     is_exhaustive: bool,
  20975     fields_val: Value,
  20976     name_strategy: Zir.Inst.NameStrategy,
  20977 ) CompileError!Air.Inst.Ref {
  20978     const pt = sema.pt;
  20979     const zcu = pt.zcu;
  20980     const gpa = sema.gpa;
  20981     const ip = &zcu.intern_pool;
  20982 
  20983     // This logic must stay in sync with the structure of `std.builtin.Type.Enum` - search for `fieldValue`.
  20984 
  20985     const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu));
  20986 
  20987     // The validation work here is non-trivial, and it's possible the type already exists.
  20988     // So in this first pass, let's just construct a hash to optimize for this case. If the
  20989     // inputs turn out to be invalid, we can cancel the WIP type later.
  20990 
  20991     // For deduplication purposes, we must create a hash including all details of this type.
  20992     // TODO: use a longer hash!
  20993     var hasher = std.hash.Wyhash.init(0);
  20994     std.hash.autoHash(&hasher, tag_ty.toIntern());
  20995     std.hash.autoHash(&hasher, is_exhaustive);
  20996     std.hash.autoHash(&hasher, fields_len);
  20997 
  20998     for (0..fields_len) |field_idx| {
  20999         const field_info = try fields_val.elemValue(pt, field_idx);
  21000 
  21001         const field_name_val = try field_info.fieldValue(pt, 0);
  21002         const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1));
  21003 
  21004         const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .enum_field_name });
  21005 
  21006         std.hash.autoHash(&hasher, .{
  21007             field_name,
  21008             field_value_val.toIntern(),
  21009         });
  21010     }
  21011 
  21012     const tracked_inst = try block.trackZir(inst);
  21013 
  21014     const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{
  21015         .has_values = true,
  21016         .tag_mode = if (is_exhaustive) .explicit else .nonexhaustive,
  21017         .fields_len = fields_len,
  21018         .key = .{ .reified = .{
  21019             .zir_index = tracked_inst,
  21020             .type_hash = hasher.final(),
  21021         } },
  21022     }, false)) {
  21023         .wip => |wip| wip,
  21024         .existing => |ty| {
  21025             try sema.declareDependency(.{ .interned = ty });
  21026             try sema.addTypeReferenceEntry(src, ty);
  21027             return Air.internedToRef(ty);
  21028         },
  21029     };
  21030     var done = false;
  21031     errdefer if (!done) wip_ty.cancel(ip, pt.tid);
  21032 
  21033     if (tag_ty.zigTypeTag(zcu) != .int) {
  21034         return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
  21035     }
  21036 
  21037     const type_name = try sema.createTypeName(
  21038         block,
  21039         name_strategy,
  21040         "enum",
  21041         inst,
  21042         wip_ty.index,
  21043     );
  21044     wip_ty.setName(ip, type_name.name, type_name.nav);
  21045 
  21046     const new_namespace_index = try pt.createNamespace(.{
  21047         .parent = block.namespace.toOptional(),
  21048         .owner_type = wip_ty.index,
  21049         .file_scope = block.getFileScopeIndex(zcu),
  21050         .generation = zcu.generation,
  21051     });
  21052 
  21053     try sema.declareDependency(.{ .interned = wip_ty.index });
  21054     try sema.addTypeReferenceEntry(src, wip_ty.index);
  21055     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
  21056     wip_ty.prepare(ip, new_namespace_index);
  21057     wip_ty.setTagTy(ip, tag_ty.toIntern());
  21058     done = true;
  21059 
  21060     for (0..fields_len) |field_idx| {
  21061         const field_info = try fields_val.elemValue(pt, field_idx);
  21062 
  21063         const field_name_val = try field_info.fieldValue(pt, 0);
  21064         const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1));
  21065 
  21066         // Don't pass a reason; first loop acts as an assertion that this is valid.
  21067         const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
  21068 
  21069         if (!try sema.intFitsInType(field_value_val, tag_ty, null)) {
  21070             // TODO: better source location
  21071             return sema.fail(block, src, "field '{f}' with enumeration value '{f}' is too large for backing int type '{f}'", .{
  21072                 field_name.fmt(ip),
  21073                 field_value_val.fmtValueSema(pt, sema),
  21074                 tag_ty.fmt(pt),
  21075             });
  21076         }
  21077 
  21078         const coerced_field_val = try pt.getCoerced(field_value_val, tag_ty);
  21079         if (wip_ty.nextField(ip, field_name, coerced_field_val.toIntern())) |conflict| {
  21080             return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) {
  21081                 .name => msg: {
  21082                     const msg = try sema.errMsg(src, "duplicate enum field '{f}'", .{field_name.fmt(ip)});
  21083                     errdefer msg.destroy(gpa);
  21084                     _ = conflict.prev_field_idx; // TODO: this note is incorrect
  21085                     try sema.errNote(src, msg, "other field here", .{});
  21086                     break :msg msg;
  21087                 },
  21088                 .value => msg: {
  21089                     const msg = try sema.errMsg(src, "enum tag value {f} already taken", .{field_value_val.fmtValueSema(pt, sema)});
  21090                     errdefer msg.destroy(gpa);
  21091                     _ = conflict.prev_field_idx; // TODO: this note is incorrect
  21092                     try sema.errNote(src, msg, "other enum tag value here", .{});
  21093                     break :msg msg;
  21094                 },
  21095             });
  21096         }
  21097     }
  21098 
  21099     if (!is_exhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(zcu)) {
  21100         return sema.fail(block, src, "non-exhaustive enum specified every value", .{});
  21101     }
  21102 
  21103     codegen_type: {
  21104         if (zcu.comp.config.use_llvm) break :codegen_type;
  21105         if (block.ownerModule().strip) break :codegen_type;
  21106         // This job depends on any resolve_type_fully jobs queued up before it.
  21107         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
  21108         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
  21109     }
  21110     return Air.internedToRef(wip_ty.index);
  21111 }
  21112 
  21113 fn reifyUnion(
  21114     sema: *Sema,
  21115     block: *Block,
  21116     inst: Zir.Inst.Index,
  21117     src: LazySrcLoc,
  21118     layout: std.builtin.Type.ContainerLayout,
  21119     opt_tag_type_val: Value,
  21120     fields_val: Value,
  21121     name_strategy: Zir.Inst.NameStrategy,
  21122 ) CompileError!Air.Inst.Ref {
  21123     const pt = sema.pt;
  21124     const zcu = pt.zcu;
  21125     const gpa = sema.gpa;
  21126     const ip = &zcu.intern_pool;
  21127 
  21128     // This logic must stay in sync with the structure of `std.builtin.Type.Union` - search for `fieldValue`.
  21129 
  21130     const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu));
  21131 
  21132     // The validation work here is non-trivial, and it's possible the type already exists.
  21133     // So in this first pass, let's just construct a hash to optimize for this case. If the
  21134     // inputs turn out to be invalid, we can cancel the WIP type later.
  21135 
  21136     // For deduplication purposes, we must create a hash including all details of this type.
  21137     // TODO: use a longer hash!
  21138     var hasher = std.hash.Wyhash.init(0);
  21139     std.hash.autoHash(&hasher, layout);
  21140     std.hash.autoHash(&hasher, opt_tag_type_val.toIntern());
  21141     std.hash.autoHash(&hasher, fields_len);
  21142 
  21143     for (0..fields_len) |field_idx| {
  21144         const field_info = try fields_val.elemValue(pt, field_idx);
  21145 
  21146         const field_name_val = try field_info.fieldValue(pt, 0);
  21147         const field_type_val = try field_info.fieldValue(pt, 1);
  21148         const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 2));
  21149 
  21150         const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .union_field_name });
  21151         std.hash.autoHash(&hasher, .{
  21152             field_name,
  21153             field_type_val.toIntern(),
  21154             field_align_val.toIntern(),
  21155         });
  21156     }
  21157 
  21158     const tracked_inst = try block.trackZir(inst);
  21159 
  21160     const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, .{
  21161         .flags = .{
  21162             .layout = layout,
  21163             .status = .none,
  21164             .runtime_tag = if (opt_tag_type_val.optionalValue(zcu) != null)
  21165                 .tagged
  21166             else if (layout != .auto)
  21167                 .none
  21168             else switch (block.wantSafeTypes()) {
  21169                 true => .safety,
  21170                 false => .none,
  21171             },
  21172             .any_aligned_fields = layout != .@"packed",
  21173             .requires_comptime = .unknown,
  21174             .assumed_runtime_bits = false,
  21175             .assumed_pointer_aligned = false,
  21176             .alignment = .none,
  21177         },
  21178         .fields_len = fields_len,
  21179         .enum_tag_ty = .none, // set later because not yet validated
  21180         .field_types = &.{}, // set later
  21181         .field_aligns = &.{}, // set later
  21182         .key = .{ .reified = .{
  21183             .zir_index = tracked_inst,
  21184             .type_hash = hasher.final(),
  21185         } },
  21186     }, false)) {
  21187         .wip => |wip| wip,
  21188         .existing => |ty| {
  21189             try sema.declareDependency(.{ .interned = ty });
  21190             try sema.addTypeReferenceEntry(src, ty);
  21191             return Air.internedToRef(ty);
  21192         },
  21193     };
  21194     errdefer wip_ty.cancel(ip, pt.tid);
  21195 
  21196     const type_name = try sema.createTypeName(
  21197         block,
  21198         name_strategy,
  21199         "union",
  21200         inst,
  21201         wip_ty.index,
  21202     );
  21203     wip_ty.setName(ip, type_name.name, type_name.nav);
  21204 
  21205     const loaded_union = ip.loadUnionType(wip_ty.index);
  21206 
  21207     const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(zcu)) |tag_type_val| tag_ty: {
  21208         switch (ip.indexToKey(tag_type_val.toIntern())) {
  21209             .enum_type => {},
  21210             else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}),
  21211         }
  21212         const enum_tag_ty = tag_type_val.toType();
  21213 
  21214         // We simply track which fields of the tag type have been seen.
  21215         const tag_ty_fields_len = enum_tag_ty.enumFieldCount(zcu);
  21216         var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len);
  21217 
  21218         for (0..fields_len) |field_idx| {
  21219             const field_info = try fields_val.elemValue(pt, field_idx);
  21220 
  21221             const field_name_val = try field_info.fieldValue(pt, 0);
  21222             const field_type_val = try field_info.fieldValue(pt, 1);
  21223             const field_alignment_val = try field_info.fieldValue(pt, 2);
  21224 
  21225             // Don't pass a reason; first loop acts as an assertion that this is valid.
  21226             const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
  21227 
  21228             const enum_index = enum_tag_ty.enumFieldIndex(field_name, zcu) orelse {
  21229                 // TODO: better source location
  21230                 return sema.fail(block, src, "no field named '{f}' in enum '{f}'", .{
  21231                     field_name.fmt(ip), enum_tag_ty.fmt(pt),
  21232                 });
  21233             };
  21234             if (seen_tags.isSet(enum_index)) {
  21235                 // TODO: better source location
  21236                 return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)});
  21237             }
  21238             seen_tags.set(enum_index);
  21239 
  21240             loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern();
  21241             const byte_align = try field_alignment_val.toUnsignedIntSema(pt);
  21242             if (layout == .@"packed") {
  21243                 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{});
  21244             } else {
  21245                 loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align);
  21246             }
  21247         }
  21248 
  21249         if (tag_ty_fields_len > fields_len) return sema.failWithOwnedErrorMsg(block, msg: {
  21250             const msg = try sema.errMsg(src, "enum fields missing in union", .{});
  21251             errdefer msg.destroy(gpa);
  21252             var it = seen_tags.iterator(.{ .kind = .unset });
  21253             while (it.next()) |enum_index| {
  21254                 const field_name = enum_tag_ty.enumFieldName(enum_index, zcu);
  21255                 try sema.addFieldErrNote(enum_tag_ty, enum_index, msg, "field '{f}' missing, declared here", .{
  21256                     field_name.fmt(ip),
  21257                 });
  21258             }
  21259             try sema.addDeclaredHereNote(msg, enum_tag_ty);
  21260             break :msg msg;
  21261         });
  21262 
  21263         break :tag_ty .{ enum_tag_ty.toIntern(), true };
  21264     } else tag_ty: {
  21265         // We must track field names and set up the tag type ourselves.
  21266         var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
  21267         try field_names.ensureTotalCapacity(sema.arena, fields_len);
  21268 
  21269         for (0..fields_len) |field_idx| {
  21270             const field_info = try fields_val.elemValue(pt, field_idx);
  21271 
  21272             const field_name_val = try field_info.fieldValue(pt, 0);
  21273             const field_type_val = try field_info.fieldValue(pt, 1);
  21274             const field_alignment_val = try field_info.fieldValue(pt, 2);
  21275 
  21276             // Don't pass a reason; first loop acts as an assertion that this is valid.
  21277             const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
  21278             const gop = field_names.getOrPutAssumeCapacity(field_name);
  21279             if (gop.found_existing) {
  21280                 // TODO: better source location
  21281                 return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)});
  21282             }
  21283 
  21284             loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern();
  21285             const byte_align = try field_alignment_val.toUnsignedIntSema(pt);
  21286             if (layout == .@"packed") {
  21287                 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{});
  21288             } else {
  21289                 loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align);
  21290             }
  21291         }
  21292 
  21293         const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), wip_ty.index, type_name.name);
  21294         break :tag_ty .{ enum_tag_ty, false };
  21295     };
  21296     errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error
  21297 
  21298     for (loaded_union.field_types.get(ip)) |field_ty_ip| {
  21299         const field_ty: Type = .fromInterned(field_ty_ip);
  21300         if (field_ty.zigTypeTag(zcu) == .@"opaque") {
  21301             return sema.failWithOwnedErrorMsg(block, msg: {
  21302                 const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  21303                 errdefer msg.destroy(gpa);
  21304 
  21305                 try sema.addDeclaredHereNote(msg, field_ty);
  21306                 break :msg msg;
  21307             });
  21308         }
  21309         if (layout == .@"extern" and !try sema.validateExternType(field_ty, .union_field)) {
  21310             return sema.failWithOwnedErrorMsg(block, msg: {
  21311                 const msg = try sema.errMsg(src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  21312                 errdefer msg.destroy(gpa);
  21313 
  21314                 try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .union_field);
  21315 
  21316                 try sema.addDeclaredHereNote(msg, field_ty);
  21317                 break :msg msg;
  21318             });
  21319         } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) {
  21320             return sema.failWithOwnedErrorMsg(block, msg: {
  21321                 const msg = try sema.errMsg(src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  21322                 errdefer msg.destroy(gpa);
  21323 
  21324                 try sema.explainWhyTypeIsNotPacked(msg, src, field_ty);
  21325 
  21326                 try sema.addDeclaredHereNote(msg, field_ty);
  21327                 break :msg msg;
  21328             });
  21329         }
  21330     }
  21331 
  21332     loaded_union.setTagType(ip, enum_tag_ty);
  21333     loaded_union.setStatus(ip, .have_field_types);
  21334 
  21335     const new_namespace_index = try pt.createNamespace(.{
  21336         .parent = block.namespace.toOptional(),
  21337         .owner_type = wip_ty.index,
  21338         .file_scope = block.getFileScopeIndex(zcu),
  21339         .generation = zcu.generation,
  21340     });
  21341 
  21342     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
  21343     codegen_type: {
  21344         if (zcu.comp.config.use_llvm) break :codegen_type;
  21345         if (block.ownerModule().strip) break :codegen_type;
  21346         // This job depends on any resolve_type_fully jobs queued up before it.
  21347         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
  21348         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
  21349     }
  21350     try sema.declareDependency(.{ .interned = wip_ty.index });
  21351     try sema.addTypeReferenceEntry(src, wip_ty.index);
  21352     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
  21353     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
  21354 }
  21355 
  21356 fn reifyTuple(
  21357     sema: *Sema,
  21358     block: *Block,
  21359     src: LazySrcLoc,
  21360     fields_val: Value,
  21361 ) CompileError!Air.Inst.Ref {
  21362     const pt = sema.pt;
  21363     const zcu = pt.zcu;
  21364     const gpa = sema.gpa;
  21365     const ip = &zcu.intern_pool;
  21366 
  21367     const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu));
  21368 
  21369     const types = try sema.arena.alloc(InternPool.Index, fields_len);
  21370     const inits = try sema.arena.alloc(InternPool.Index, fields_len);
  21371 
  21372     for (types, inits, 0..) |*field_ty, *field_init, field_idx| {
  21373         const field_info = try fields_val.elemValue(pt, field_idx);
  21374 
  21375         const field_name_val = try field_info.fieldValue(pt, 0);
  21376         const field_type_val = try field_info.fieldValue(pt, 1);
  21377         const field_default_value_val = try field_info.fieldValue(pt, 2);
  21378         const field_is_comptime_val = try field_info.fieldValue(pt, 3);
  21379         const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4));
  21380 
  21381         const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .tuple_field_name });
  21382         const field_type = field_type_val.toType();
  21383         const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: {
  21384             const ptr_ty = try pt.singleConstPtrType(field_type_val.toType());
  21385             // We need to do this deref here, so we won't check for this error case later on.
  21386             const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime(
  21387                 block,
  21388                 src,
  21389                 .{ .simple = .tuple_field_default_value },
  21390             );
  21391             // Resolve the value so that lazy values do not create distinct types.
  21392             break :d (try sema.resolveLazyValue(val)).toIntern();
  21393         } else .none;
  21394 
  21395         const field_name_index = field_name.toUnsigned(ip) orelse return sema.fail(
  21396             block,
  21397             src,
  21398             "tuple cannot have non-numeric field '{f}'",
  21399             .{field_name.fmt(ip)},
  21400         );
  21401         if (field_name_index != field_idx) {
  21402             return sema.fail(
  21403                 block,
  21404                 src,
  21405                 "tuple field name '{d}' does not match field index {d}",
  21406                 .{ field_name_index, field_idx },
  21407             );
  21408         }
  21409 
  21410         try sema.validateTupleFieldType(block, field_type, src);
  21411 
  21412         {
  21413             const alignment_ok = ok: {
  21414                 if (field_alignment_val.toIntern() == .zero) break :ok true;
  21415                 const given_align = try field_alignment_val.getUnsignedIntSema(pt) orelse break :ok false;
  21416                 const abi_align = (try field_type.abiAlignmentSema(pt)).toByteUnits() orelse 0;
  21417                 break :ok abi_align == given_align;
  21418             };
  21419             if (!alignment_ok) {
  21420                 return sema.fail(block, src, "tuple fields cannot specify alignment", .{});
  21421             }
  21422         }
  21423 
  21424         if (field_is_comptime_val.toBool() and field_default_value == .none) {
  21425             return sema.fail(block, src, "comptime field without default initialization value", .{});
  21426         }
  21427 
  21428         if (!field_is_comptime_val.toBool() and field_default_value != .none) {
  21429             return sema.fail(block, src, "non-comptime tuple fields cannot specify default initialization value", .{});
  21430         }
  21431 
  21432         const default_or_opv: InternPool.Index = default: {
  21433             if (field_default_value != .none) {
  21434                 break :default field_default_value;
  21435             }
  21436             if (try sema.typeHasOnePossibleValue(field_type)) |opv| {
  21437                 break :default opv.toIntern();
  21438             }
  21439             break :default .none;
  21440         };
  21441 
  21442         field_ty.* = field_type.toIntern();
  21443         field_init.* = default_or_opv;
  21444     }
  21445 
  21446     return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{
  21447         .types = types,
  21448         .values = inits,
  21449     }));
  21450 }
  21451 
  21452 fn reifyStruct(
  21453     sema: *Sema,
  21454     block: *Block,
  21455     inst: Zir.Inst.Index,
  21456     src: LazySrcLoc,
  21457     layout: std.builtin.Type.ContainerLayout,
  21458     opt_backing_int_val: Value,
  21459     fields_val: Value,
  21460     name_strategy: Zir.Inst.NameStrategy,
  21461 ) CompileError!Air.Inst.Ref {
  21462     const pt = sema.pt;
  21463     const zcu = pt.zcu;
  21464     const gpa = sema.gpa;
  21465     const ip = &zcu.intern_pool;
  21466 
  21467     // This logic must stay in sync with the structure of `std.builtin.Type.Struct` - search for `fieldValue`.
  21468 
  21469     const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu));
  21470 
  21471     // The validation work here is non-trivial, and it's possible the type already exists.
  21472     // So in this first pass, let's just construct a hash to optimize for this case. If the
  21473     // inputs turn out to be invalid, we can cancel the WIP type later.
  21474 
  21475     // For deduplication purposes, we must create a hash including all details of this type.
  21476     // TODO: use a longer hash!
  21477     var hasher = std.hash.Wyhash.init(0);
  21478     std.hash.autoHash(&hasher, layout);
  21479     std.hash.autoHash(&hasher, opt_backing_int_val.toIntern());
  21480     std.hash.autoHash(&hasher, fields_len);
  21481 
  21482     var any_comptime_fields = false;
  21483     var any_default_inits = false;
  21484 
  21485     for (0..fields_len) |field_idx| {
  21486         const field_info = try fields_val.elemValue(pt, field_idx);
  21487 
  21488         const field_name_val = try field_info.fieldValue(pt, 0);
  21489         const field_type_val = try field_info.fieldValue(pt, 1);
  21490         const field_default_value_val = try field_info.fieldValue(pt, 2);
  21491         const field_is_comptime_val = try field_info.fieldValue(pt, 3);
  21492         const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4));
  21493 
  21494         const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .struct_field_name });
  21495         const field_is_comptime = field_is_comptime_val.toBool();
  21496         const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: {
  21497             const ptr_ty = try pt.singleConstPtrType(field_type_val.toType());
  21498             // We need to do this deref here, so we won't check for this error case later on.
  21499             const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime(
  21500                 block,
  21501                 src,
  21502                 .{ .simple = .struct_field_default_value },
  21503             );
  21504             // Resolve the value so that lazy values do not create distinct types.
  21505             break :d (try sema.resolveLazyValue(val)).toIntern();
  21506         } else .none;
  21507 
  21508         std.hash.autoHash(&hasher, .{
  21509             field_name,
  21510             field_type_val.toIntern(),
  21511             field_default_value,
  21512             field_is_comptime,
  21513             field_alignment_val.toIntern(),
  21514         });
  21515 
  21516         if (field_is_comptime) any_comptime_fields = true;
  21517         if (field_default_value != .none) any_default_inits = true;
  21518     }
  21519 
  21520     const tracked_inst = try block.trackZir(inst);
  21521 
  21522     const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
  21523         .layout = layout,
  21524         .fields_len = fields_len,
  21525         .known_non_opv = false,
  21526         .requires_comptime = .unknown,
  21527         .any_comptime_fields = any_comptime_fields,
  21528         .any_default_inits = any_default_inits,
  21529         .any_aligned_fields = layout != .@"packed",
  21530         .inits_resolved = true,
  21531         .key = .{ .reified = .{
  21532             .zir_index = tracked_inst,
  21533             .type_hash = hasher.final(),
  21534         } },
  21535     }, false)) {
  21536         .wip => |wip| wip,
  21537         .existing => |ty| {
  21538             try sema.declareDependency(.{ .interned = ty });
  21539             try sema.addTypeReferenceEntry(src, ty);
  21540             return Air.internedToRef(ty);
  21541         },
  21542     };
  21543     errdefer wip_ty.cancel(ip, pt.tid);
  21544 
  21545     const type_name = try sema.createTypeName(
  21546         block,
  21547         name_strategy,
  21548         "struct",
  21549         inst,
  21550         wip_ty.index,
  21551     );
  21552     wip_ty.setName(ip, type_name.name, type_name.nav);
  21553 
  21554     const struct_type = ip.loadStructType(wip_ty.index);
  21555 
  21556     for (0..fields_len) |field_idx| {
  21557         const field_info = try fields_val.elemValue(pt, field_idx);
  21558 
  21559         const field_name_val = try field_info.fieldValue(pt, 0);
  21560         const field_type_val = try field_info.fieldValue(pt, 1);
  21561         const field_default_value_val = try field_info.fieldValue(pt, 2);
  21562         const field_is_comptime_val = try field_info.fieldValue(pt, 3);
  21563         const field_alignment_val = try field_info.fieldValue(pt, 4);
  21564 
  21565         const field_ty = field_type_val.toType();
  21566         // Don't pass a reason; first loop acts as an assertion that this is valid.
  21567         const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
  21568         if (struct_type.addFieldName(ip, field_name)) |prev_index| {
  21569             _ = prev_index; // TODO: better source location
  21570             return sema.fail(block, src, "duplicate struct field name {f}", .{field_name.fmt(ip)});
  21571         }
  21572 
  21573         if (!try sema.intFitsInType(field_alignment_val, align_ty, null)) {
  21574             return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)});
  21575         }
  21576         const byte_align = try field_alignment_val.toUnsignedIntSema(pt);
  21577         if (layout == .@"packed") {
  21578             if (byte_align != 0) return sema.fail(block, src, "alignment of a packed struct field must be set to 0", .{});
  21579         } else {
  21580             struct_type.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align);
  21581         }
  21582 
  21583         const field_is_comptime = field_is_comptime_val.toBool();
  21584         if (field_is_comptime) {
  21585             assert(any_comptime_fields);
  21586             switch (layout) {
  21587                 .@"extern" => return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}),
  21588                 .@"packed" => return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}),
  21589                 .auto => struct_type.setFieldComptime(ip, field_idx),
  21590             }
  21591         }
  21592 
  21593         const field_default: InternPool.Index = d: {
  21594             if (!any_default_inits) break :d .none;
  21595             const ptr_val = field_default_value_val.optionalValue(zcu) orelse break :d .none;
  21596             const ptr_ty = try pt.singleConstPtrType(field_ty);
  21597             // Asserted comptime-dereferencable above.
  21598             const val = (try sema.pointerDeref(block, src, ptr_val, ptr_ty)).?;
  21599             // We already resolved this for deduplication, so we may as well do it now.
  21600             break :d (try sema.resolveLazyValue(val)).toIntern();
  21601         };
  21602 
  21603         if (field_is_comptime and field_default == .none) {
  21604             return sema.fail(block, src, "comptime field without default initialization value", .{});
  21605         }
  21606 
  21607         struct_type.field_types.get(ip)[field_idx] = field_type_val.toIntern();
  21608         if (field_default != .none) {
  21609             struct_type.field_inits.get(ip)[field_idx] = field_default;
  21610         }
  21611 
  21612         if (field_ty.zigTypeTag(zcu) == .@"opaque") {
  21613             return sema.failWithOwnedErrorMsg(block, msg: {
  21614                 const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  21615                 errdefer msg.destroy(gpa);
  21616 
  21617                 try sema.addDeclaredHereNote(msg, field_ty);
  21618                 break :msg msg;
  21619             });
  21620         }
  21621         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  21622             return sema.failWithOwnedErrorMsg(block, msg: {
  21623                 const msg = try sema.errMsg(src, "struct fields cannot be 'noreturn'", .{});
  21624                 errdefer msg.destroy(gpa);
  21625 
  21626                 try sema.addDeclaredHereNote(msg, field_ty);
  21627                 break :msg msg;
  21628             });
  21629         }
  21630         if (layout == .@"extern" and !try sema.validateExternType(field_ty, .struct_field)) {
  21631             return sema.failWithOwnedErrorMsg(block, msg: {
  21632                 const msg = try sema.errMsg(src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  21633                 errdefer msg.destroy(gpa);
  21634 
  21635                 try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .struct_field);
  21636 
  21637                 try sema.addDeclaredHereNote(msg, field_ty);
  21638                 break :msg msg;
  21639             });
  21640         } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) {
  21641             return sema.failWithOwnedErrorMsg(block, msg: {
  21642                 const msg = try sema.errMsg(src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  21643                 errdefer msg.destroy(gpa);
  21644 
  21645                 try sema.explainWhyTypeIsNotPacked(msg, src, field_ty);
  21646 
  21647                 try sema.addDeclaredHereNote(msg, field_ty);
  21648                 break :msg msg;
  21649             });
  21650         }
  21651     }
  21652 
  21653     if (layout == .@"packed") {
  21654         var fields_bit_sum: u64 = 0;
  21655         for (0..struct_type.field_types.len) |field_idx| {
  21656             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_idx]);
  21657             field_ty.resolveLayout(pt) catch |err| switch (err) {
  21658                 error.AnalysisFail => {
  21659                     const msg = sema.err orelse return err;
  21660                     try sema.errNote(src, msg, "while checking a field of this struct", .{});
  21661                     return err;
  21662                 },
  21663                 else => return err,
  21664             };
  21665             fields_bit_sum += field_ty.bitSize(zcu);
  21666         }
  21667 
  21668         if (opt_backing_int_val.optionalValue(zcu)) |backing_int_val| {
  21669             const backing_int_ty = backing_int_val.toType();
  21670             try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum);
  21671             struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
  21672         } else {
  21673             const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum));
  21674             struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
  21675         }
  21676     }
  21677 
  21678     const new_namespace_index = try pt.createNamespace(.{
  21679         .parent = block.namespace.toOptional(),
  21680         .owner_type = wip_ty.index,
  21681         .file_scope = block.getFileScopeIndex(zcu),
  21682         .generation = zcu.generation,
  21683     });
  21684 
  21685     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
  21686     codegen_type: {
  21687         if (zcu.comp.config.use_llvm) break :codegen_type;
  21688         if (block.ownerModule().strip) break :codegen_type;
  21689         // This job depends on any resolve_type_fully jobs queued up before it.
  21690         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
  21691         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
  21692     }
  21693     try sema.declareDependency(.{ .interned = wip_ty.index });
  21694     try sema.addTypeReferenceEntry(src, wip_ty.index);
  21695     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
  21696     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
  21697 }
  21698 
  21699 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
  21700     const pt = sema.pt;
  21701     const va_list_ty = try sema.getBuiltinType(src, .VaList);
  21702     const va_list_ptr = try pt.singleMutPtrType(va_list_ty);
  21703 
  21704     const inst = try sema.resolveInst(zir_ref);
  21705     return sema.coerce(block, va_list_ptr, inst, src);
  21706 }
  21707 
  21708 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21709     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  21710     const src = block.nodeOffset(extra.node);
  21711     const va_list_src = block.builtinCallArgSrc(extra.node, 0);
  21712     const ty_src = block.builtinCallArgSrc(extra.node, 1);
  21713 
  21714     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);
  21715     const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);
  21716 
  21717     if (!try sema.validateExternType(arg_ty, .param_ty)) {
  21718         const msg = msg: {
  21719             const msg = try sema.errMsg(ty_src, "cannot get '{f}' from variadic argument", .{arg_ty.fmt(sema.pt)});
  21720             errdefer msg.destroy(sema.gpa);
  21721 
  21722             try sema.explainWhyTypeIsNotExtern(msg, ty_src, arg_ty, .param_ty);
  21723 
  21724             try sema.addDeclaredHereNote(msg, arg_ty);
  21725             break :msg msg;
  21726         };
  21727         return sema.failWithOwnedErrorMsg(block, msg);
  21728     }
  21729 
  21730     try sema.requireRuntimeBlock(block, src, null);
  21731     return block.addTyOp(.c_va_arg, arg_ty, va_list_ref);
  21732 }
  21733 
  21734 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21735     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  21736     const src = block.nodeOffset(extra.node);
  21737     const va_list_src = block.builtinCallArgSrc(extra.node, 0);
  21738 
  21739     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  21740     const va_list_ty = try sema.getBuiltinType(src, .VaList);
  21741 
  21742     try sema.requireRuntimeBlock(block, src, null);
  21743     return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref);
  21744 }
  21745 
  21746 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21747     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  21748     const src = block.nodeOffset(extra.node);
  21749     const va_list_src = block.builtinCallArgSrc(extra.node, 0);
  21750 
  21751     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  21752 
  21753     try sema.requireRuntimeBlock(block, src, null);
  21754     return block.addUnOp(.c_va_end, va_list_ref);
  21755 }
  21756 
  21757 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21758     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  21759     const src = block.nodeOffset(src_node);
  21760 
  21761     const va_list_ty = try sema.getBuiltinType(src, .VaList);
  21762     try sema.requireRuntimeBlock(block, src, null);
  21763     return block.addInst(.{
  21764         .tag = .c_va_start,
  21765         .data = .{ .ty = va_list_ty },
  21766     });
  21767 }
  21768 
  21769 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21770     const pt = sema.pt;
  21771     const zcu = pt.zcu;
  21772     const ip = &zcu.intern_pool;
  21773 
  21774     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  21775     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  21776     const ty = try sema.resolveType(block, ty_src, inst_data.operand);
  21777 
  21778     const type_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{f}", .{ty.fmt(pt)}, .no_embedded_nulls);
  21779     return sema.addNullTerminatedStrLit(type_name);
  21780 }
  21781 
  21782 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21783     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  21784     const src = block.nodeOffset(inst_data.src_node);
  21785     return sema.failWithUseOfAsync(block, src);
  21786 }
  21787 
  21788 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21789     const pt = sema.pt;
  21790     const zcu = pt.zcu;
  21791     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21792     const src = block.nodeOffset(inst_data.src_node);
  21793     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21794     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  21795     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat");
  21796     const operand = try sema.resolveInst(extra.rhs);
  21797     const operand_ty = sema.typeOf(operand);
  21798 
  21799     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
  21800     const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
  21801 
  21802     const dest_scalar_ty = dest_ty.scalarType(zcu);
  21803     const operand_scalar_ty = operand_ty.scalarType(zcu);
  21804 
  21805     _ = try sema.checkIntType(block, src, dest_scalar_ty);
  21806     try sema.checkFloatType(block, operand_src, operand_scalar_ty);
  21807 
  21808     if (try sema.resolveValue(operand)) |operand_val| {
  21809         const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate);
  21810         return Air.internedToRef(result_val.toIntern());
  21811     } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
  21812         return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int });
  21813     }
  21814 
  21815     try sema.requireRuntimeBlock(block, src, operand_src);
  21816     if (dest_scalar_ty.intInfo(zcu).bits == 0) {
  21817         if (block.wantSafety()) {
  21818             // Emit an explicit safety check. We can do this one like `abs(x) < 1`.
  21819             const abs_ref = try block.addTyOp(.abs, operand_ty, operand);
  21820             const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref;
  21821             const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern());
  21822             const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref);
  21823             try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds);
  21824         }
  21825         const scalar_val = try pt.intValue(dest_scalar_ty, 0);
  21826         return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern());
  21827     }
  21828     if (block.wantSafety()) {
  21829         try sema.preparePanicId(src, .integer_part_out_of_bounds);
  21830         return block.addTyOp(switch (block.float_mode) {
  21831             .optimized => .int_from_float_optimized_safe,
  21832             .strict => .int_from_float_safe,
  21833         }, dest_ty, operand);
  21834     }
  21835     return block.addTyOp(switch (block.float_mode) {
  21836         .optimized => .int_from_float_optimized,
  21837         .strict => .int_from_float,
  21838     }, dest_ty, operand);
  21839 }
  21840 
  21841 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21842     const pt = sema.pt;
  21843     const zcu = pt.zcu;
  21844     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21845     const src = block.nodeOffset(inst_data.src_node);
  21846     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21847     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  21848     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt");
  21849     const operand = try sema.resolveInst(extra.rhs);
  21850     const operand_ty = sema.typeOf(operand);
  21851 
  21852     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
  21853 
  21854     const dest_scalar_ty = dest_ty.scalarType(zcu);
  21855     const operand_scalar_ty = operand_ty.scalarType(zcu);
  21856 
  21857     try sema.checkFloatType(block, src, dest_scalar_ty);
  21858     _ = try sema.checkIntType(block, operand_src, operand_scalar_ty);
  21859 
  21860     if (try sema.resolveValue(operand)) |operand_val| {
  21861         const result_val = try operand_val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, pt, .sema);
  21862         return Air.internedToRef(result_val.toIntern());
  21863     } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) {
  21864         return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_float });
  21865     }
  21866 
  21867     try sema.requireRuntimeBlock(block, src, operand_src);
  21868     return block.addTyOp(.float_from_int, dest_ty, operand);
  21869 }
  21870 
  21871 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21872     const pt = sema.pt;
  21873     const zcu = pt.zcu;
  21874     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21875     const src = block.nodeOffset(inst_data.src_node);
  21876 
  21877     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21878 
  21879     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  21880     const operand_res = try sema.resolveInst(extra.rhs);
  21881 
  21882     const uncoerced_operand_ty = sema.typeOf(operand_res);
  21883     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt");
  21884     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, uncoerced_operand_ty, src, operand_src);
  21885 
  21886     const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
  21887     const operand_ty: Type = if (is_vector) operand_ty: {
  21888         const len = dest_ty.vectorLen(zcu);
  21889         break :operand_ty try pt.vectorType(.{ .child = .usize_type, .len = len });
  21890     } else .usize;
  21891 
  21892     const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src);
  21893 
  21894     const ptr_ty = dest_ty.scalarType(zcu);
  21895     try sema.checkPtrType(block, src, ptr_ty, true);
  21896 
  21897     const elem_ty = ptr_ty.elemType2(zcu);
  21898     const ptr_align = try ptr_ty.ptrAlignmentSema(pt);
  21899 
  21900     if (ptr_ty.isSlice(zcu)) {
  21901         const msg = msg: {
  21902             const msg = try sema.errMsg(src, "integer cannot be converted to slice type '{f}'", .{ptr_ty.fmt(pt)});
  21903             errdefer msg.destroy(sema.gpa);
  21904             try sema.errNote(src, msg, "slice length cannot be inferred from address", .{});
  21905             break :msg msg;
  21906         };
  21907         return sema.failWithOwnedErrorMsg(block, msg);
  21908     }
  21909 
  21910     if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
  21911         if (!is_vector) {
  21912             const ptr_val = try sema.ptrFromIntVal(block, operand_src, val, ptr_ty, ptr_align, null);
  21913             return Air.internedToRef(ptr_val.toIntern());
  21914         }
  21915         const len = dest_ty.vectorLen(zcu);
  21916         const new_elems = try sema.arena.alloc(InternPool.Index, len);
  21917         for (new_elems, 0..) |*new_elem, elem_idx| {
  21918             const elem = try val.elemValue(pt, elem_idx);
  21919             const ptr_val = try sema.ptrFromIntVal(block, operand_src, elem, ptr_ty, ptr_align, elem_idx);
  21920             new_elem.* = ptr_val.toIntern();
  21921         }
  21922         return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern());
  21923     }
  21924     if (try ptr_ty.comptimeOnlySema(pt)) {
  21925         return sema.failWithOwnedErrorMsg(block, msg: {
  21926             const msg = try sema.errMsg(src, "pointer to comptime-only type '{f}' must be comptime-known, but operand is runtime-known", .{ptr_ty.fmt(pt)});
  21927             errdefer msg.destroy(sema.gpa);
  21928 
  21929             try sema.explainWhyTypeIsComptime(msg, src, ptr_ty);
  21930             break :msg msg;
  21931         });
  21932     }
  21933     try sema.requireRuntimeBlock(block, src, operand_src);
  21934     try sema.checkLogicalPtrOperation(block, src, ptr_ty);
  21935     if (block.wantSafety() and (try elem_ty.hasRuntimeBitsSema(pt) or elem_ty.zigTypeTag(zcu) == .@"fn")) {
  21936         if (!ptr_ty.isAllowzeroPtr(zcu)) {
  21937             const is_non_zero = if (is_vector) all_non_zero: {
  21938                 const zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern());
  21939                 const is_non_zero = try block.addCmpVector(operand_coerced, zero_usize, .neq);
  21940                 break :all_non_zero try block.addReduce(is_non_zero, .And);
  21941             } else try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
  21942             try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null);
  21943         }
  21944         if (ptr_align.compare(.gt, .@"1")) {
  21945             const align_bytes_minus_1 = ptr_align.toByteUnits().? - 1;
  21946             const align_mask = Air.internedToRef((try sema.splat(operand_ty, try pt.intValue(
  21947                 .usize,
  21948                 if (elem_ty.fnPtrMaskOrNull(zcu)) |mask|
  21949                     align_bytes_minus_1 & mask
  21950                 else
  21951                     align_bytes_minus_1,
  21952             ))).toIntern());
  21953             const remainder = try block.addBinOp(.bit_and, operand_coerced, align_mask);
  21954             const is_aligned = if (is_vector) all_aligned: {
  21955                 const splat_zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern());
  21956                 const is_aligned = try block.addCmpVector(remainder, splat_zero_usize, .eq);
  21957                 break :all_aligned try block.addReduce(is_aligned, .And);
  21958             } else try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  21959             try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment);
  21960         }
  21961     }
  21962     return block.addBitCast(dest_ty, operand_coerced);
  21963 }
  21964 
  21965 fn ptrFromIntVal(
  21966     sema: *Sema,
  21967     block: *Block,
  21968     operand_src: LazySrcLoc,
  21969     operand_val: Value,
  21970     ptr_ty: Type,
  21971     ptr_align: Alignment,
  21972     vec_idx: ?usize,
  21973 ) !Value {
  21974     const pt = sema.pt;
  21975     const zcu = pt.zcu;
  21976     if (operand_val.isUndef(zcu)) {
  21977         if (ptr_ty.isAllowzeroPtr(zcu) and ptr_align == .@"1") {
  21978             return pt.undefValue(ptr_ty);
  21979         }
  21980         return sema.failWithUseOfUndef(block, operand_src, vec_idx);
  21981     }
  21982     const addr = try operand_val.toUnsignedIntSema(pt);
  21983     if (!ptr_ty.isAllowzeroPtr(zcu) and addr == 0)
  21984         return sema.fail(block, operand_src, "pointer type '{f}' does not allow address zero", .{ptr_ty.fmt(pt)});
  21985     if (addr != 0 and ptr_align != .none) {
  21986         const masked_addr = if (ptr_ty.childType(zcu).fnPtrMaskOrNull(zcu)) |mask|
  21987             addr & mask
  21988         else
  21989             addr;
  21990 
  21991         if (!ptr_align.check(masked_addr)) {
  21992             return sema.fail(block, operand_src, "pointer type '{f}' requires aligned address", .{ptr_ty.fmt(pt)});
  21993         }
  21994     }
  21995 
  21996     return switch (ptr_ty.zigTypeTag(zcu)) {
  21997         .optional => Value.fromInterned(try pt.intern(.{ .opt = .{
  21998             .ty = ptr_ty.toIntern(),
  21999             .val = if (addr == 0) .none else (try pt.ptrIntValue(ptr_ty.childType(zcu), addr)).toIntern(),
  22000         } })),
  22001         .pointer => try pt.ptrIntValue(ptr_ty, addr),
  22002         else => unreachable,
  22003     };
  22004 }
  22005 
  22006 fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  22007     const pt = sema.pt;
  22008     const zcu = pt.zcu;
  22009     const ip = &zcu.intern_pool;
  22010     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  22011     const src = block.nodeOffset(extra.node);
  22012     const operand_src = block.builtinCallArgSrc(extra.node, 0);
  22013     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast");
  22014     const operand = try sema.resolveInst(extra.rhs);
  22015     const operand_ty = sema.typeOf(operand);
  22016 
  22017     const dest_tag = dest_ty.zigTypeTag(zcu);
  22018     const operand_tag = operand_ty.zigTypeTag(zcu);
  22019 
  22020     if (dest_tag != .error_set and dest_tag != .error_union) {
  22021         return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)});
  22022     }
  22023     if (operand_tag != .error_set and operand_tag != .error_union) {
  22024         return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(operand_tag)});
  22025     }
  22026     if (dest_tag == .error_set and operand_tag == .error_union) {
  22027         return sema.fail(block, src, "cannot cast an error union type to error set", .{});
  22028     }
  22029     if (dest_tag == .error_union and operand_tag == .error_union and
  22030         dest_ty.errorUnionPayload(zcu).toIntern() != operand_ty.errorUnionPayload(zcu).toIntern())
  22031     {
  22032         return sema.failWithOwnedErrorMsg(block, msg: {
  22033             const msg = try sema.errMsg(src, "payload types of error unions must match", .{});
  22034             errdefer msg.destroy(sema.gpa);
  22035             const dest_payload_ty = dest_ty.errorUnionPayload(zcu);
  22036             const operand_payload_ty = operand_ty.errorUnionPayload(zcu);
  22037             try sema.errNote(src, msg, "destination payload is '{f}'", .{dest_payload_ty.fmt(pt)});
  22038             try sema.errNote(src, msg, "operand payload is '{f}'", .{operand_payload_ty.fmt(pt)});
  22039             try addDeclaredHereNote(sema, msg, dest_ty);
  22040             try addDeclaredHereNote(sema, msg, operand_ty);
  22041             break :msg msg;
  22042         });
  22043     }
  22044     const dest_err_ty = switch (dest_tag) {
  22045         .error_union => dest_ty.errorUnionSet(zcu),
  22046         .error_set => dest_ty,
  22047         else => unreachable,
  22048     };
  22049     const operand_err_ty = switch (operand_tag) {
  22050         .error_union => operand_ty.errorUnionSet(zcu),
  22051         .error_set => operand_ty,
  22052         else => unreachable,
  22053     };
  22054 
  22055     const disjoint = disjoint: {
  22056         // Try avoiding resolving inferred error sets if we can
  22057         if (!dest_err_ty.isAnyError(zcu) and dest_err_ty.errorSetIsEmpty(zcu)) break :disjoint true;
  22058         if (!operand_err_ty.isAnyError(zcu) and operand_err_ty.errorSetIsEmpty(zcu)) break :disjoint true;
  22059         if (dest_err_ty.isAnyError(zcu)) break :disjoint false;
  22060         if (operand_err_ty.isAnyError(zcu)) break :disjoint false;
  22061         const dest_err_names = dest_err_ty.errorSetNames(zcu);
  22062         for (0..dest_err_names.len) |dest_err_index| {
  22063             if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
  22064                 break :disjoint false;
  22065         }
  22066 
  22067         if (!ip.isInferredErrorSetType(dest_err_ty.toIntern()) and
  22068             !ip.isInferredErrorSetType(operand_err_ty.toIntern()))
  22069         {
  22070             break :disjoint true;
  22071         }
  22072 
  22073         _ = try sema.resolveInferredErrorSetTy(block, src, dest_err_ty.toIntern());
  22074         _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_err_ty.toIntern());
  22075         for (0..dest_err_names.len) |dest_err_index| {
  22076             if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
  22077                 break :disjoint false;
  22078         }
  22079 
  22080         break :disjoint true;
  22081     };
  22082     if (disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) {
  22083         return sema.fail(block, src, "error sets '{f}' and '{f}' have no common errors", .{
  22084             operand_err_ty.fmt(pt), dest_err_ty.fmt(pt),
  22085         });
  22086     }
  22087 
  22088     // operand must be defined since it can be an invalid error value
  22089     if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
  22090         const err_name: InternPool.NullTerminatedString = switch (operand_tag) {
  22091             .error_set => ip.indexToKey(operand_val.toIntern()).err.name,
  22092             .error_union => switch (ip.indexToKey(operand_val.toIntern()).error_union.val) {
  22093                 .err_name => |name| name,
  22094                 .payload => |payload_val| {
  22095                     assert(dest_tag == .error_union); // should be guaranteed from the type checks above
  22096                     return sema.coerce(block, dest_ty, Air.internedToRef(payload_val), operand_src);
  22097                 },
  22098             },
  22099             else => unreachable,
  22100         };
  22101 
  22102         if (!dest_err_ty.isAnyError(zcu) and !Type.errorSetHasFieldIp(ip, dest_err_ty.toIntern(), err_name)) {
  22103             return sema.fail(block, src, "'error.{f}' not a member of error set '{f}'", .{
  22104                 err_name.fmt(ip), dest_err_ty.fmt(pt),
  22105             });
  22106         }
  22107 
  22108         return Air.internedToRef(try pt.intern(switch (dest_tag) {
  22109             .error_set => .{ .err = .{
  22110                 .ty = dest_ty.toIntern(),
  22111                 .name = err_name,
  22112             } },
  22113             .error_union => .{ .error_union = .{
  22114                 .ty = dest_ty.toIntern(),
  22115                 .val = .{ .err_name = err_name },
  22116             } },
  22117             else => unreachable,
  22118         }));
  22119     }
  22120 
  22121     const err_int_ty = try pt.errorIntType();
  22122     if (block.wantSafety() and !dest_err_ty.isAnyError(zcu) and
  22123         dest_err_ty.toIntern() != .adhoc_inferred_error_set_type and
  22124         zcu.backendSupportsFeature(.error_set_has_value))
  22125     {
  22126         const err_code_inst = switch (operand_tag) {
  22127             .error_set => operand,
  22128             .error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand),
  22129             else => unreachable,
  22130         };
  22131         const err_int_inst = try block.addBitCast(err_int_ty, err_code_inst);
  22132 
  22133         if (dest_tag == .error_union) {
  22134             const zero_err = try pt.intRef(err_int_ty, 0);
  22135             const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err);
  22136             if (disjoint) {
  22137                 // Error must be zero.
  22138                 try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code);
  22139             } else {
  22140                 // Error must be in destination set or zero.
  22141                 const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
  22142                 const ok = try block.addBinOp(.bool_or, has_value, is_zero);
  22143                 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
  22144             }
  22145         } else {
  22146             const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
  22147             try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
  22148         }
  22149     }
  22150 
  22151     if (operand_tag == .error_set and dest_tag == .error_union) {
  22152         const err_val = try block.addBitCast(dest_err_ty, operand);
  22153         return block.addTyOp(.wrap_errunion_err, dest_ty, err_val);
  22154     } else {
  22155         return block.addBitCast(dest_ty, operand);
  22156     }
  22157 }
  22158 
  22159 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  22160     const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
  22161     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small)));
  22162     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  22163     const src = block.nodeOffset(extra.node);
  22164     const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node });
  22165     const operand = try sema.resolveInst(extra.rhs);
  22166     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName());
  22167     return sema.ptrCastFull(
  22168         block,
  22169         flags,
  22170         src,
  22171         operand,
  22172         operand_src,
  22173         dest_ty,
  22174         flags.needResultTypeBuiltinName(),
  22175     );
  22176 }
  22177 
  22178 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22179     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  22180     const src = block.nodeOffset(inst_data.src_node);
  22181     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22182     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22183     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrCast");
  22184     const operand = try sema.resolveInst(extra.rhs);
  22185 
  22186     return sema.ptrCastFull(
  22187         block,
  22188         .{ .ptr_cast = true },
  22189         src,
  22190         operand,
  22191         operand_src,
  22192         dest_ty,
  22193         "@ptrCast",
  22194     );
  22195 }
  22196 
  22197 fn ptrCastFull(
  22198     sema: *Sema,
  22199     block: *Block,
  22200     flags: Zir.Inst.FullPtrCastFlags,
  22201     src: LazySrcLoc,
  22202     operand: Air.Inst.Ref,
  22203     operand_src: LazySrcLoc,
  22204     dest_ty: Type,
  22205     operation: []const u8,
  22206 ) CompileError!Air.Inst.Ref {
  22207     const pt = sema.pt;
  22208     const zcu = pt.zcu;
  22209     const operand_ty = sema.typeOf(operand);
  22210 
  22211     try sema.checkPtrType(block, src, dest_ty, true);
  22212     try sema.checkPtrOperand(block, operand_src, operand_ty);
  22213 
  22214     const src_info = operand_ty.ptrInfo(zcu);
  22215     const dest_info = dest_ty.ptrInfo(zcu);
  22216 
  22217     try Type.fromInterned(src_info.child).resolveLayout(pt);
  22218     try Type.fromInterned(dest_info.child).resolveLayout(pt);
  22219 
  22220     const DestSliceLen = union(enum) {
  22221         undef,
  22222         constant: u64,
  22223         equal_runtime_src_slice,
  22224         change_runtime_src_slice: struct {
  22225             bytes_per_src: u64,
  22226             bytes_per_dest: u64,
  22227         },
  22228     };
  22229     // Populated iff the destination type is a slice.
  22230     const dest_slice_len: ?DestSliceLen = len: {
  22231         switch (dest_info.flags.size) {
  22232             .slice => {},
  22233             .many, .c, .one => break :len null,
  22234         }
  22235         // A `null` length means the operand is a runtime-known slice (so the length is runtime-known).
  22236         // `src_elem_type` is different from `src_info.child` if the latter is an array, to ensure we ignore sentinels.
  22237         const src_elem_ty: Type, const opt_src_len: ?u64 = switch (src_info.flags.size) {
  22238             .one => src: {
  22239                 const true_child: Type = .fromInterned(src_info.child);
  22240                 break :src switch (true_child.zigTypeTag(zcu)) {
  22241                     .array => .{ true_child.childType(zcu), true_child.arrayLen(zcu) },
  22242                     else => .{ true_child, 1 },
  22243                 };
  22244             },
  22245             .slice => src: {
  22246                 const operand_val = try sema.resolveValue(operand) orelse break :src .{ .fromInterned(src_info.child), null };
  22247                 if (operand_val.isUndef(zcu)) break :len .undef;
  22248                 const slice_val = switch (operand_ty.zigTypeTag(zcu)) {
  22249                     .optional => operand_val.optionalValue(zcu) orelse break :len .undef,
  22250                     .pointer => operand_val,
  22251                     else => unreachable,
  22252                 };
  22253                 const slice_len_resolved = try sema.resolveLazyValue(.fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern())));
  22254                 if (slice_len_resolved.isUndef(zcu)) break :len .undef;
  22255                 break :src .{ .fromInterned(src_info.child), slice_len_resolved.toUnsignedInt(zcu) };
  22256             },
  22257             .many, .c => {
  22258                 return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)});
  22259             },
  22260         };
  22261         const dest_elem_ty: Type = .fromInterned(dest_info.child);
  22262         if (dest_elem_ty.toIntern() == src_elem_ty.toIntern()) {
  22263             break :len if (opt_src_len) |l| .{ .constant = l } else .equal_runtime_src_slice;
  22264         }
  22265         if (!src_elem_ty.comptimeOnly(zcu) and !dest_elem_ty.comptimeOnly(zcu)) {
  22266             const src_elem_size = src_elem_ty.abiSize(zcu);
  22267             const dest_elem_size = dest_elem_ty.abiSize(zcu);
  22268             if (dest_elem_size == 0) {
  22269                 return sema.fail(block, src, "cannot infer length of slice of zero-bit '{f}' from '{f}'", .{
  22270                     dest_elem_ty.fmt(pt), operand_ty.fmt(pt),
  22271                 });
  22272             }
  22273             if (opt_src_len) |src_len| {
  22274                 const bytes = src_len * src_elem_size;
  22275                 const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch switch (src_info.flags.size) {
  22276                     .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}),
  22277                     .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{Type.fromInterned(src_info.child).fmt(pt)}),
  22278                     else => unreachable,
  22279                 };
  22280                 break :len .{ .constant = dest_len };
  22281             }
  22282             assert(src_info.flags.size == .slice);
  22283             break :len .{ .change_runtime_src_slice = .{
  22284                 .bytes_per_src = src_elem_size,
  22285                 .bytes_per_dest = dest_elem_size,
  22286             } };
  22287         }
  22288         // We apply rules for comptime memory consistent with comptime loads/stores, where arrays of
  22289         // comptime-only types can be "restructured".
  22290         const dest_base_ty: Type, const dest_base_per_elem: u64 = dest_elem_ty.arrayBase(zcu);
  22291         const src_base_ty: Type, const src_base_per_elem: u64 = src_elem_ty.arrayBase(zcu);
  22292         // The source value has `src_len * src_base_per_elem` values of type `src_base_ty`.
  22293         // The result value will have `dest_len * dest_base_per_elem` values of type `dest_base_ty`.
  22294         if (dest_base_ty.toIntern() != src_base_ty.toIntern()) {
  22295             return sema.fail(block, src, "cannot infer length of comptime-only '{f}' from incompatible '{f}'", .{
  22296                 dest_ty.fmt(pt), operand_ty.fmt(pt),
  22297             });
  22298         }
  22299         // `src_base_ty` is comptime-only, so `src_elem_ty` is comptime-only, so `operand_ty` is
  22300         // comptime-only, so `operand` is comptime-known, so `opt_src_len` is non-`null`.
  22301         const src_len = opt_src_len.?;
  22302         const base_len = src_len * src_base_per_elem;
  22303         const dest_len = std.math.divExact(u64, base_len, dest_base_per_elem) catch switch (src_info.flags.size) {
  22304             .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}),
  22305             .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{src_elem_ty.fmt(pt)}),
  22306             else => unreachable,
  22307         };
  22308         break :len .{ .constant = dest_len };
  22309     };
  22310 
  22311     // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs
  22312 
  22313     if (!flags.ptr_cast) {
  22314         const is_array_ptr_to_slice = b: {
  22315             if (dest_info.flags.size != .slice) break :b false;
  22316             if (src_info.flags.size != .one) break :b false;
  22317             const src_pointer_child: Type = .fromInterned(src_info.child);
  22318             if (src_pointer_child.zigTypeTag(zcu) != .array) break :b false;
  22319             const src_elem = src_pointer_child.childType(zcu);
  22320             break :b src_elem.toIntern() == dest_info.child;
  22321         };
  22322 
  22323         check_size: {
  22324             if (src_info.flags.size == dest_info.flags.size) break :check_size;
  22325             if (is_array_ptr_to_slice) break :check_size;
  22326             if (src_info.flags.size == .c) break :check_size;
  22327             if (dest_info.flags.size == .c) break :check_size;
  22328             return sema.failWithOwnedErrorMsg(block, msg: {
  22329                 const msg = try sema.errMsg(src, "cannot implicitly convert {s} to {s}", .{
  22330                     pointerSizeString(src_info.flags.size),
  22331                     pointerSizeString(dest_info.flags.size),
  22332                 });
  22333                 errdefer msg.destroy(sema.gpa);
  22334                 if (dest_info.flags.size == .many and
  22335                     (src_info.flags.size == .slice or
  22336                         (src_info.flags.size == .one and Type.fromInterned(src_info.child).zigTypeTag(zcu) == .array)))
  22337                 {
  22338                     try sema.errNote(src, msg, "use 'ptr' field to convert slice to many pointer", .{});
  22339                 } else {
  22340                     try sema.errNote(src, msg, "use @ptrCast to change pointer size", .{});
  22341                 }
  22342                 break :msg msg;
  22343             });
  22344         }
  22345 
  22346         check_child: {
  22347             const src_child: Type = if (dest_info.flags.size == .slice and src_info.flags.size == .one) blk: {
  22348                 // *[n]T -> []T
  22349                 break :blk Type.fromInterned(src_info.child).childType(zcu);
  22350             } else .fromInterned(src_info.child);
  22351 
  22352             const dest_child: Type = .fromInterned(dest_info.child);
  22353 
  22354             const imc_res = try sema.coerceInMemoryAllowed(
  22355                 block,
  22356                 dest_child,
  22357                 src_child,
  22358                 !dest_info.flags.is_const,
  22359                 zcu.getTarget(),
  22360                 src,
  22361                 operand_src,
  22362                 null,
  22363             );
  22364             if (imc_res == .ok) break :check_child;
  22365             return sema.failWithOwnedErrorMsg(block, msg: {
  22366                 const msg = try sema.errMsg(src, "pointer element type '{f}' cannot coerce into element type '{f}'", .{
  22367                     src_child.fmt(pt), dest_child.fmt(pt),
  22368                 });
  22369                 errdefer msg.destroy(sema.gpa);
  22370                 try imc_res.report(sema, src, msg);
  22371                 try sema.errNote(src, msg, "use @ptrCast to cast pointer element type", .{});
  22372                 break :msg msg;
  22373             });
  22374         }
  22375 
  22376         check_sent: {
  22377             if (dest_info.sentinel == .none) break :check_sent;
  22378             if (src_info.flags.size == .c) break :check_sent;
  22379             if (src_info.sentinel != .none) {
  22380                 const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_info.sentinel, dest_info.child);
  22381                 if (dest_info.sentinel == coerced_sent) break :check_sent;
  22382             }
  22383             if (is_array_ptr_to_slice) {
  22384                 // [*]nT -> []T
  22385                 const arr_ty: Type = .fromInterned(src_info.child);
  22386                 if (arr_ty.sentinel(zcu)) |src_sentinel| {
  22387                     const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_sentinel.toIntern(), dest_info.child);
  22388                     if (dest_info.sentinel == coerced_sent) break :check_sent;
  22389                 }
  22390             }
  22391             return sema.failWithOwnedErrorMsg(block, msg: {
  22392                 const msg = if (src_info.sentinel == .none) blk: {
  22393                     break :blk try sema.errMsg(src, "destination pointer requires '{f}' sentinel", .{
  22394                         Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema),
  22395                     });
  22396                 } else blk: {
  22397                     break :blk try sema.errMsg(src, "pointer sentinel '{f}' cannot coerce into pointer sentinel '{f}'", .{
  22398                         Value.fromInterned(src_info.sentinel).fmtValueSema(pt, sema),
  22399                         Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema),
  22400                     });
  22401                 };
  22402                 errdefer msg.destroy(sema.gpa);
  22403                 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{});
  22404                 break :msg msg;
  22405             });
  22406         }
  22407 
  22408         if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) {
  22409             return sema.failWithOwnedErrorMsg(block, msg: {
  22410                 const msg = try sema.errMsg(src, "pointer host size '{d}' cannot coerce into pointer host size '{d}'", .{
  22411                     src_info.packed_offset.host_size,
  22412                     dest_info.packed_offset.host_size,
  22413                 });
  22414                 errdefer msg.destroy(sema.gpa);
  22415                 try sema.errNote(src, msg, "use @ptrCast to cast pointer host size", .{});
  22416                 break :msg msg;
  22417             });
  22418         }
  22419 
  22420         if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) {
  22421             return sema.failWithOwnedErrorMsg(block, msg: {
  22422                 const msg = try sema.errMsg(src, "pointer bit offset '{d}' cannot coerce into pointer bit offset '{d}'", .{
  22423                     src_info.packed_offset.bit_offset,
  22424                     dest_info.packed_offset.bit_offset,
  22425                 });
  22426                 errdefer msg.destroy(sema.gpa);
  22427                 try sema.errNote(src, msg, "use @ptrCast to cast pointer bit offset", .{});
  22428                 break :msg msg;
  22429             });
  22430         }
  22431 
  22432         check_allowzero: {
  22433             const src_allows_zero = operand_ty.ptrAllowsZero(zcu);
  22434             const dest_allows_zero = dest_ty.ptrAllowsZero(zcu);
  22435             if (!src_allows_zero) break :check_allowzero;
  22436             if (dest_allows_zero) break :check_allowzero;
  22437 
  22438             return sema.failWithOwnedErrorMsg(block, msg: {
  22439                 const msg = try sema.errMsg(src, "'{f}' could have null values which are illegal in type '{f}'", .{
  22440                     operand_ty.fmt(pt),
  22441                     dest_ty.fmt(pt),
  22442                 });
  22443                 errdefer msg.destroy(sema.gpa);
  22444                 try sema.errNote(src, msg, "use @ptrCast to assert the pointer is not null", .{});
  22445                 break :msg msg;
  22446             });
  22447         }
  22448 
  22449         // TODO: vector index?
  22450     }
  22451 
  22452     const src_align = if (src_info.flags.alignment != .none)
  22453         src_info.flags.alignment
  22454     else
  22455         Type.fromInterned(src_info.child).abiAlignment(zcu);
  22456 
  22457     const dest_align = if (dest_info.flags.alignment != .none)
  22458         dest_info.flags.alignment
  22459     else
  22460         Type.fromInterned(dest_info.child).abiAlignment(zcu);
  22461 
  22462     if (!flags.align_cast) {
  22463         if (dest_align.compare(.gt, src_align)) {
  22464             return sema.failWithOwnedErrorMsg(block, msg: {
  22465                 const msg = try sema.errMsg(src, "{s} increases pointer alignment", .{operation});
  22466                 errdefer msg.destroy(sema.gpa);
  22467                 try sema.errNote(operand_src, msg, "'{f}' has alignment '{d}'", .{
  22468                     operand_ty.fmt(pt), src_align.toByteUnits() orelse 0,
  22469                 });
  22470                 try sema.errNote(src, msg, "'{f}' has alignment '{d}'", .{
  22471                     dest_ty.fmt(pt), dest_align.toByteUnits() orelse 0,
  22472                 });
  22473                 try sema.errNote(src, msg, "use @alignCast to assert pointer alignment", .{});
  22474                 break :msg msg;
  22475             });
  22476         }
  22477     }
  22478 
  22479     if (!flags.addrspace_cast) {
  22480         if (src_info.flags.address_space != dest_info.flags.address_space) {
  22481             return sema.failWithOwnedErrorMsg(block, msg: {
  22482                 const msg = try sema.errMsg(src, "{s} changes pointer address space", .{operation});
  22483                 errdefer msg.destroy(sema.gpa);
  22484                 try sema.errNote(operand_src, msg, "'{f}' has address space '{s}'", .{
  22485                     operand_ty.fmt(pt), @tagName(src_info.flags.address_space),
  22486                 });
  22487                 try sema.errNote(src, msg, "'{f}' has address space '{s}'", .{
  22488                     dest_ty.fmt(pt), @tagName(dest_info.flags.address_space),
  22489                 });
  22490                 try sema.errNote(src, msg, "use @addrSpaceCast to cast pointer address space", .{});
  22491                 break :msg msg;
  22492             });
  22493         }
  22494     } else {
  22495         // Some address space casts are always disallowed
  22496         if (!target_util.addrSpaceCastIsValid(zcu.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) {
  22497             return sema.failWithOwnedErrorMsg(block, msg: {
  22498                 const msg = try sema.errMsg(src, "invalid address space cast", .{});
  22499                 errdefer msg.destroy(sema.gpa);
  22500                 try sema.errNote(operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{
  22501                     @tagName(src_info.flags.address_space),
  22502                     @tagName(dest_info.flags.address_space),
  22503                 });
  22504                 break :msg msg;
  22505             });
  22506         }
  22507     }
  22508 
  22509     if (!flags.const_cast) {
  22510         if (src_info.flags.is_const and !dest_info.flags.is_const) {
  22511             return sema.failWithOwnedErrorMsg(block, msg: {
  22512                 const msg = try sema.errMsg(src, "{s} discards const qualifier", .{operation});
  22513                 errdefer msg.destroy(sema.gpa);
  22514                 try sema.errNote(src, msg, "use @constCast to discard const qualifier", .{});
  22515                 break :msg msg;
  22516             });
  22517         }
  22518     }
  22519 
  22520     if (!flags.volatile_cast) {
  22521         if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) {
  22522             return sema.failWithOwnedErrorMsg(block, msg: {
  22523                 const msg = try sema.errMsg(src, "{s} discards volatile qualifier", .{operation});
  22524                 errdefer msg.destroy(sema.gpa);
  22525                 try sema.errNote(src, msg, "use @volatileCast to discard volatile qualifier", .{});
  22526                 break :msg msg;
  22527             });
  22528         }
  22529     }
  22530 
  22531     // Type validation done -- this cast is okay. Let's do it!
  22532     //
  22533     // `operand` is a maybe-optional pointer or slice.
  22534     // `dest_ty` is a maybe-optional pointer or slice.
  22535     //
  22536     // We have a few safety checks:
  22537     // * if the destination does not allow zero, check the operand is not null / 0
  22538     // * if the destination is more aligned than the operand, check the pointer alignment
  22539     // * if `slice_needs_len_change`, check the element count divides neatly
  22540 
  22541     ct: {
  22542         if (flags.addrspace_cast) break :ct; // cannot `@addrSpaceCast` at comptime
  22543         const operand_val = try sema.resolveValue(operand) orelse break :ct;
  22544 
  22545         if (operand_val.isUndef(zcu)) {
  22546             if (!dest_ty.ptrAllowsZero(zcu)) {
  22547                 return sema.failWithUseOfUndef(block, operand_src, null);
  22548             }
  22549             return pt.undefRef(dest_ty);
  22550         }
  22551 
  22552         if (operand_val.isNull(zcu)) {
  22553             if (!dest_ty.ptrAllowsZero(zcu)) {
  22554                 return sema.fail(block, operand_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)});
  22555             }
  22556             if (dest_ty.zigTypeTag(zcu) == .optional) {
  22557                 return Air.internedToRef((try pt.nullValue(dest_ty)).toIntern());
  22558             } else {
  22559                 return Air.internedToRef((try pt.ptrIntValue(dest_ty, 0)).toIntern());
  22560             }
  22561         }
  22562 
  22563         const ptr_val: Value = switch (src_info.flags.size) {
  22564             .slice => .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.ptr),
  22565             .one, .many, .c => operand_val,
  22566         };
  22567 
  22568         if (dest_align.compare(.gt, src_align)) {
  22569             if (try ptr_val.getUnsignedIntSema(pt)) |addr| {
  22570                 const masked_addr = if (Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu)) |mask|
  22571                     addr & mask
  22572                 else
  22573                     addr;
  22574 
  22575                 if (!dest_align.check(masked_addr)) {
  22576                     return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{
  22577                         addr,
  22578                         dest_align.toByteUnits().?,
  22579                     });
  22580                 }
  22581             }
  22582         }
  22583 
  22584         if (dest_info.flags.size == .slice) {
  22585             // Because the operand is comptime-known and not `null`, the slice length has already been computed:
  22586             const len: Value = switch (dest_slice_len.?) {
  22587                 .undef => .undef_usize,
  22588                 .constant => |n| try pt.intValue(.usize, n),
  22589                 .equal_runtime_src_slice => unreachable,
  22590                 .change_runtime_src_slice => unreachable,
  22591             };
  22592             return Air.internedToRef(try pt.intern(.{ .slice = .{
  22593                 .ty = dest_ty.toIntern(),
  22594                 .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(),
  22595                 .len = len.toIntern(),
  22596             } }));
  22597         } else {
  22598             // Any to non-slice
  22599             const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty);
  22600             return Air.internedToRef(new_ptr_val.toIntern());
  22601         }
  22602     }
  22603 
  22604     try sema.validateRuntimeValue(block, operand_src, operand);
  22605 
  22606     const can_cast_to_int = !target_util.arePointersLogical(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));
  22607     const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu);
  22608     const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align);
  22609 
  22610     const slice_needs_len_change = if (dest_slice_len) |l| switch (l) {
  22611         .undef, .equal_runtime_src_slice => false,
  22612         .constant, .change_runtime_src_slice => true,
  22613     } else false;
  22614 
  22615     // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer.
  22616     const need_operand_ptr = src_info.flags.size != .slice or // we already have it
  22617         dest_info.flags.size != .slice or // the result is a raw pointer
  22618         need_null_check or // safety check happens on pointer
  22619         need_align_check or // safety check happens on pointer
  22620         flags.addrspace_cast or // AIR addrspace_cast acts on a pointer
  22621         slice_needs_len_change; // to change the length, we reconstruct the slice
  22622 
  22623     // This is not quite just the pointer part of `operand` -- it's also had the address space cast done already.
  22624     const operand_ptr: Air.Inst.Ref = ptr: {
  22625         if (!need_operand_ptr) break :ptr .none;
  22626         // First, just get the pointer.
  22627         const pre_addrspace_cast = inner: {
  22628             if (src_info.flags.size != .slice) break :inner operand;
  22629             if (operand_ty.zigTypeTag(zcu) == .optional) {
  22630                 break :inner try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty);
  22631             } else {
  22632                 break :inner try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty);
  22633             }
  22634         };
  22635         // Now, do an addrspace cast if necessary!
  22636         if (!flags.addrspace_cast) break :ptr pre_addrspace_cast;
  22637 
  22638         const intermediate_ptr_ty = try pt.ptrTypeSema(info: {
  22639             var info = src_info;
  22640             info.flags.address_space = dest_info.flags.address_space;
  22641             break :info info;
  22642         });
  22643         const intermediate_ty = if (operand_ty.zigTypeTag(zcu) == .optional) blk: {
  22644             break :blk try pt.optionalType(intermediate_ptr_ty.toIntern());
  22645         } else intermediate_ptr_ty;
  22646         break :ptr try block.addInst(.{
  22647             .tag = .addrspace_cast,
  22648             .data = .{ .ty_op = .{
  22649                 .ty = Air.internedToRef(intermediate_ty.toIntern()),
  22650                 .operand = pre_addrspace_cast,
  22651             } },
  22652         });
  22653     };
  22654 
  22655     // Whether we need to know if the (slice) operand has `len == 0`.
  22656     const need_operand_len_is_zero = src_info.flags.size == .slice and
  22657         dest_info.flags.size == .slice and
  22658         (need_null_check or need_align_check);
  22659     // Whether we need to get the (slice) operand's `len`.
  22660     const need_operand_len = need_len: {
  22661         if (src_info.flags.size != .slice) break :need_len false;
  22662         if (dest_info.flags.size != .slice) break :need_len false;
  22663         if (need_operand_len_is_zero) break :need_len true;
  22664         if (flags.addrspace_cast or slice_needs_len_change) break :need_len true;
  22665         break :need_len false;
  22666     };
  22667     // `.none` if `!need_operand_len`.
  22668     const operand_len: Air.Inst.Ref = len: {
  22669         if (!need_operand_len) break :len .none;
  22670         break :len try block.addTyOp(.slice_len, .usize, operand);
  22671     };
  22672     // `.none` if `!need_operand_len_is_zero`.
  22673     const operand_len_is_zero: Air.Inst.Ref = zero: {
  22674         if (!need_operand_len_is_zero) break :zero .none;
  22675         assert(need_operand_len);
  22676         break :zero try block.addBinOp(.cmp_eq, operand_len, .zero_usize);
  22677     };
  22678 
  22679     // `operand_ptr` converted to an integer, for safety checks.
  22680     const operand_ptr_int: Air.Inst.Ref = if (need_null_check or need_align_check) i: {
  22681         assert(need_operand_ptr);
  22682         break :i try block.addBitCast(.usize, operand_ptr);
  22683     } else .none;
  22684 
  22685     if (need_null_check) {
  22686         assert(operand_ptr_int != .none);
  22687         const ptr_is_non_zero = try block.addBinOp(.cmp_neq, operand_ptr_int, .zero_usize);
  22688         const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: {
  22689             break :ok try block.addBinOp(.bool_or, operand_len_is_zero, ptr_is_non_zero);
  22690         } else ptr_is_non_zero;
  22691         try sema.addSafetyCheck(block, src, ok, .cast_to_null);
  22692     }
  22693     if (need_align_check) {
  22694         assert(operand_ptr_int != .none);
  22695         const align_mask = try pt.intRef(.usize, mask: {
  22696             const target_ptr_mask = Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu) orelse ~@as(u64, 0);
  22697             break :mask (dest_align.toByteUnits().? - 1) & target_ptr_mask;
  22698         });
  22699         const ptr_masked = try block.addBinOp(.bit_and, operand_ptr_int, align_mask);
  22700         const is_aligned = try block.addBinOp(.cmp_eq, ptr_masked, .zero_usize);
  22701         const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: {
  22702             break :ok try block.addBinOp(.bool_or, operand_len_is_zero, is_aligned);
  22703         } else is_aligned;
  22704         try sema.addSafetyCheck(block, src, ok, .incorrect_alignment);
  22705     }
  22706 
  22707     if (dest_info.flags.size == .slice) {
  22708         if (src_info.flags.size == .slice and !flags.addrspace_cast and !slice_needs_len_change) {
  22709             // Fast path: just bitcast!
  22710             return block.addBitCast(dest_ty, operand);
  22711         }
  22712 
  22713         // We need to deconstruct the slice (if applicable) and reconstruct it.
  22714         assert(need_operand_ptr);
  22715 
  22716         const result_len: Air.Inst.Ref = switch (dest_slice_len.?) {
  22717             .undef => .undef_usize,
  22718             .constant => |n| try pt.intRef(.usize, n),
  22719             .equal_runtime_src_slice => len: {
  22720                 assert(need_operand_len);
  22721                 break :len operand_len;
  22722             },
  22723             .change_runtime_src_slice => |change| len: {
  22724                 assert(need_operand_len);
  22725                 // If `mul / div` is a whole number, then just multiply the length by it.
  22726                 if (std.math.divExact(u64, change.bytes_per_src, change.bytes_per_dest)) |dest_per_src| {
  22727                     const multiplier = try pt.intRef(.usize, dest_per_src);
  22728                     break :len try block.addBinOp(.mul, operand_len, multiplier);
  22729                 } else |err| switch (err) {
  22730                     error.DivisionByZero => unreachable,
  22731                     error.UnexpectedRemainder => {}, // fall through to code below
  22732                 }
  22733                 // If `div / mul` is a whole number, then just divide the length by it.
  22734                 // This incurs a safety check.
  22735                 if (std.math.divExact(u64, change.bytes_per_dest, change.bytes_per_src)) |src_per_dest| {
  22736                     const divisor = try pt.intRef(.usize, src_per_dest);
  22737                     if (block.wantSafety()) {
  22738                         // Check that the element count divides neatly.
  22739                         const remainder = try block.addBinOp(.rem, operand_len, divisor);
  22740                         const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  22741                         try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len});
  22742                     }
  22743                     break :len try block.addBinOp(.div_exact, operand_len, divisor);
  22744                 } else |err| switch (err) {
  22745                     error.DivisionByZero => unreachable,
  22746                     error.UnexpectedRemainder => {}, // fall through to code below
  22747                 }
  22748                 // Fallback: the elements don't divide easily. We'll multiply *and* divide. This incurs a safety check.
  22749                 const total_bytes_ref = try block.addBinOp(.mul, operand_len, try pt.intRef(.usize, change.bytes_per_src));
  22750                 const bytes_per_dest_ref = try pt.intRef(.usize, change.bytes_per_dest);
  22751                 if (block.wantSafety()) {
  22752                     // Check that `total_bytes_ref` divides neatly into `bytes_per_dest_ref`.
  22753                     const remainder = try block.addBinOp(.rem, total_bytes_ref, bytes_per_dest_ref);
  22754                     const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  22755                     try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len});
  22756                 }
  22757                 break :len try block.addBinOp(.div_exact, total_bytes_ref, bytes_per_dest_ref);
  22758             },
  22759         };
  22760 
  22761         const operand_ptr_ty = sema.typeOf(operand_ptr);
  22762         const want_ptr_ty = switch (dest_ty.zigTypeTag(zcu)) {
  22763             .optional => try pt.optionalType(dest_ty.childType(zcu).slicePtrFieldType(zcu).toIntern()),
  22764             .pointer => dest_ty.slicePtrFieldType(zcu),
  22765             else => unreachable,
  22766         };
  22767         const coerced_ptr = if (operand_ptr_ty.toIntern() != want_ptr_ty.toIntern()) ptr: {
  22768             break :ptr try block.addBitCast(want_ptr_ty, operand_ptr);
  22769         } else operand_ptr;
  22770 
  22771         return block.addInst(.{
  22772             .tag = .slice,
  22773             .data = .{ .ty_pl = .{
  22774                 .ty = Air.internedToRef(dest_ty.toIntern()),
  22775                 .payload = try sema.addExtra(Air.Bin{
  22776                     .lhs = coerced_ptr,
  22777                     .rhs = result_len,
  22778                 }),
  22779             } },
  22780         });
  22781     } else {
  22782         assert(need_operand_ptr);
  22783         // We just need to bitcast the pointer, if necessary.
  22784         // It might not be necessary, since we might have just needed the `addrspace_cast`.
  22785         const result = if (sema.typeOf(operand_ptr).toIntern() == dest_ty.toIntern())
  22786             operand_ptr
  22787         else
  22788             try block.addBitCast(dest_ty, operand_ptr);
  22789 
  22790         try sema.checkKnownAllocPtr(block, operand, result);
  22791         return result;
  22792     }
  22793 }
  22794 
  22795 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  22796     const pt = sema.pt;
  22797     const zcu = pt.zcu;
  22798     const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
  22799     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small)));
  22800     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  22801     const src = block.nodeOffset(extra.node);
  22802     const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node });
  22803     const operand = try sema.resolveInst(extra.operand);
  22804     const operand_ty = sema.typeOf(operand);
  22805     try sema.checkPtrOperand(block, operand_src, operand_ty);
  22806 
  22807     var ptr_info = operand_ty.ptrInfo(zcu);
  22808     if (flags.const_cast) ptr_info.flags.is_const = false;
  22809     if (flags.volatile_cast) ptr_info.flags.is_volatile = false;
  22810 
  22811     const dest_ty = blk: {
  22812         const dest_ty = try pt.ptrTypeSema(ptr_info);
  22813         if (operand_ty.zigTypeTag(zcu) == .optional) {
  22814             break :blk try pt.optionalType(dest_ty.toIntern());
  22815         }
  22816         break :blk dest_ty;
  22817     };
  22818 
  22819     if (try sema.resolveValue(operand)) |operand_val| {
  22820         return Air.internedToRef((try pt.getCoerced(operand_val, dest_ty)).toIntern());
  22821     }
  22822 
  22823     try sema.requireRuntimeBlock(block, src, null);
  22824     const new_ptr = try block.addBitCast(dest_ty, operand);
  22825     try sema.checkKnownAllocPtr(block, operand, new_ptr);
  22826     return new_ptr;
  22827 }
  22828 
  22829 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22830     const pt = sema.pt;
  22831     const zcu = pt.zcu;
  22832     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  22833     const src = block.nodeOffset(inst_data.src_node);
  22834     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22835     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22836     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate");
  22837     const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src);
  22838     const operand = try sema.resolveInst(extra.rhs);
  22839     const operand_ty = sema.typeOf(operand);
  22840     const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
  22841 
  22842     const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector;
  22843     const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector;
  22844     if (operand_is_vector != dest_is_vector) {
  22845         return sema.fail(block, operand_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), operand_ty.fmt(pt) });
  22846     }
  22847 
  22848     if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
  22849         return sema.coerce(block, dest_ty, operand, operand_src);
  22850     }
  22851 
  22852     const dest_info = dest_scalar_ty.intInfo(zcu);
  22853 
  22854     if (try sema.typeHasOnePossibleValue(dest_ty)) |val| {
  22855         return Air.internedToRef(val.toIntern());
  22856     }
  22857 
  22858     if (operand_scalar_ty.zigTypeTag(zcu) != .comptime_int) {
  22859         const operand_info = operand_ty.intInfo(zcu);
  22860         if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22861             return Air.internedToRef(val.toIntern());
  22862         }
  22863 
  22864         if (operand_info.signedness != dest_info.signedness) {
  22865             return sema.fail(block, operand_src, "expected {s} integer type, found '{f}'", .{
  22866                 @tagName(dest_info.signedness), operand_ty.fmt(pt),
  22867             });
  22868         }
  22869         switch (std.math.order(dest_info.bits, operand_info.bits)) {
  22870             .gt => {
  22871                 const msg = msg: {
  22872                     const msg = try sema.errMsg(
  22873                         src,
  22874                         "destination type '{f}' has more bits than source type '{f}'",
  22875                         .{ dest_ty.fmt(pt), operand_ty.fmt(pt) },
  22876                     );
  22877                     errdefer msg.destroy(sema.gpa);
  22878                     try sema.errNote(src, msg, "destination type has {d} bits", .{
  22879                         dest_info.bits,
  22880                     });
  22881                     try sema.errNote(operand_src, msg, "operand type has {d} bits", .{
  22882                         operand_info.bits,
  22883                     });
  22884                     break :msg msg;
  22885                 };
  22886                 return sema.failWithOwnedErrorMsg(block, msg);
  22887             },
  22888             .eq => return operand,
  22889             .lt => {},
  22890         }
  22891     }
  22892 
  22893     if (try sema.resolveValueResolveLazy(operand)) |val| {
  22894         const result_val = try arith.truncate(sema, val, operand_ty, dest_ty, dest_info.signedness, dest_info.bits);
  22895         return Air.internedToRef(result_val.toIntern());
  22896     }
  22897 
  22898     try sema.requireRuntimeBlock(block, src, operand_src);
  22899     return block.addTyOp(.trunc, dest_ty, operand);
  22900 }
  22901 
  22902 fn zirBitCount(
  22903     sema: *Sema,
  22904     block: *Block,
  22905     inst: Zir.Inst.Index,
  22906     air_tag: Air.Inst.Tag,
  22907     comptime comptimeOp: fn (val: Value, ty: Type, zcu: *Zcu) u64,
  22908 ) CompileError!Air.Inst.Ref {
  22909     const pt = sema.pt;
  22910     const zcu = pt.zcu;
  22911     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  22912     const src = block.nodeOffset(inst_data.src_node);
  22913     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22914     const operand = try sema.resolveInst(inst_data.operand);
  22915     const operand_ty = sema.typeOf(operand);
  22916     _ = try sema.checkIntOrVector(block, operand, operand_src);
  22917     const bits = operand_ty.intInfo(zcu).bits;
  22918 
  22919     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22920         return Air.internedToRef(val.toIntern());
  22921     }
  22922 
  22923     const result_scalar_ty = try pt.smallestUnsignedInt(bits);
  22924     switch (operand_ty.zigTypeTag(zcu)) {
  22925         .vector => {
  22926             const vec_len = operand_ty.vectorLen(zcu);
  22927             const result_ty = try pt.vectorType(.{
  22928                 .len = vec_len,
  22929                 .child = result_scalar_ty.toIntern(),
  22930             });
  22931             if (try sema.resolveValue(operand)) |val| {
  22932                 if (val.isUndef(zcu)) return pt.undefRef(result_ty);
  22933 
  22934                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  22935                 const scalar_ty = operand_ty.scalarType(zcu);
  22936                 for (elems, 0..) |*elem, i| {
  22937                     const elem_val = try val.elemValue(pt, i);
  22938                     const count = comptimeOp(elem_val, scalar_ty, zcu);
  22939                     elem.* = (try pt.intValue(result_scalar_ty, count)).toIntern();
  22940                 }
  22941                 return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern());
  22942             } else {
  22943                 try sema.requireRuntimeBlock(block, src, operand_src);
  22944                 return block.addTyOp(air_tag, result_ty, operand);
  22945             }
  22946         },
  22947         .int => {
  22948             if (try sema.resolveValueResolveLazy(operand)) |val| {
  22949                 if (val.isUndef(zcu)) return pt.undefRef(result_scalar_ty);
  22950                 return pt.intRef(result_scalar_ty, comptimeOp(val, operand_ty, zcu));
  22951             } else {
  22952                 try sema.requireRuntimeBlock(block, src, operand_src);
  22953                 return block.addTyOp(air_tag, result_scalar_ty, operand);
  22954             }
  22955         },
  22956         else => unreachable,
  22957     }
  22958 }
  22959 
  22960 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22961     const pt = sema.pt;
  22962     const zcu = pt.zcu;
  22963     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  22964     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22965     const operand = try sema.resolveInst(inst_data.operand);
  22966     const operand_ty = sema.typeOf(operand);
  22967     const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
  22968     const bits = scalar_ty.intInfo(zcu).bits;
  22969     if (bits % 8 != 0) {
  22970         return sema.fail(
  22971             block,
  22972             operand_src,
  22973             "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {d} bits",
  22974             .{ scalar_ty.fmt(pt), bits },
  22975         );
  22976     }
  22977     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22978         return .fromValue(val);
  22979     }
  22980     if (try sema.resolveValue(operand)) |operand_val| {
  22981         return .fromValue(try arith.byteSwap(sema, operand_val, operand_ty));
  22982     }
  22983     return block.addTyOp(.byte_swap, operand_ty, operand);
  22984 }
  22985 
  22986 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22987     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  22988     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22989     const operand = try sema.resolveInst(inst_data.operand);
  22990     const operand_ty = sema.typeOf(operand);
  22991     _ = try sema.checkIntOrVector(block, operand, operand_src);
  22992 
  22993     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22994         return .fromValue(val);
  22995     }
  22996     if (try sema.resolveValue(operand)) |operand_val| {
  22997         return .fromValue(try arith.bitReverse(sema, operand_val, operand_ty));
  22998     }
  22999     return block.addTyOp(.bit_reverse, operand_ty, operand);
  23000 }
  23001 
  23002 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23003     const offset = try sema.bitOffsetOf(block, inst);
  23004     return sema.pt.intRef(.comptime_int, offset);
  23005 }
  23006 
  23007 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23008     const offset = try sema.bitOffsetOf(block, inst);
  23009     // TODO reminder to make this a compile error for packed structs
  23010     return sema.pt.intRef(.comptime_int, offset / 8);
  23011 }
  23012 
  23013 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
  23014     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23015     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  23016     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  23017     const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  23018     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23019 
  23020     const ty = try sema.resolveType(block, ty_src, extra.lhs);
  23021     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name });
  23022 
  23023     const pt = sema.pt;
  23024     const zcu = pt.zcu;
  23025     const ip = &zcu.intern_pool;
  23026     try ty.resolveLayout(pt);
  23027     switch (ty.zigTypeTag(zcu)) {
  23028         .@"struct" => {},
  23029         else => return sema.fail(block, ty_src, "expected struct type, found '{f}'", .{ty.fmt(pt)}),
  23030     }
  23031 
  23032     const field_index = if (ty.isTuple(zcu)) blk: {
  23033         if (field_name.eqlSlice("len", ip)) {
  23034             return sema.fail(block, src, "no offset available for 'len' field of tuple", .{});
  23035         }
  23036         break :blk try sema.tupleFieldIndex(block, ty, field_name, field_name_src);
  23037     } else try sema.structFieldIndex(block, ty, field_name, field_name_src);
  23038 
  23039     if (ty.structFieldIsComptime(field_index, zcu)) {
  23040         return sema.fail(block, src, "no offset available for comptime field", .{});
  23041     }
  23042 
  23043     switch (ty.containerLayout(zcu)) {
  23044         .@"packed" => {
  23045             var bit_sum: u64 = 0;
  23046             const struct_type = ip.loadStructType(ty.toIntern());
  23047             for (0..struct_type.field_types.len) |i| {
  23048                 if (i == field_index) {
  23049                     return bit_sum;
  23050                 }
  23051                 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  23052                 bit_sum += field_ty.bitSize(zcu);
  23053             } else unreachable;
  23054         },
  23055         else => return ty.structFieldOffset(field_index, zcu) * 8,
  23056     }
  23057 }
  23058 
  23059 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
  23060     const pt = sema.pt;
  23061     const zcu = pt.zcu;
  23062     switch (ty.zigTypeTag(zcu)) {
  23063         .@"struct", .@"enum", .@"union", .@"opaque" => return,
  23064         else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{f}'", .{ty.fmt(pt)}),
  23065     }
  23066 }
  23067 
  23068 /// Returns `true` if the type was a comptime_int.
  23069 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
  23070     const pt = sema.pt;
  23071     const zcu = pt.zcu;
  23072     switch (ty.zigTypeTag(zcu)) {
  23073         .comptime_int => return true,
  23074         .int => return false,
  23075         else => return sema.fail(block, src, "expected integer type, found '{f}'", .{ty.fmt(pt)}),
  23076     }
  23077 }
  23078 
  23079 fn checkInvalidPtrIntArithmetic(
  23080     sema: *Sema,
  23081     block: *Block,
  23082     src: LazySrcLoc,
  23083     ty: Type,
  23084 ) CompileError!void {
  23085     const pt = sema.pt;
  23086     const zcu = pt.zcu;
  23087     switch (ty.zigTypeTag(zcu)) {
  23088         .pointer => switch (ty.ptrSize(zcu)) {
  23089             .one, .slice => return,
  23090             .many, .c => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
  23091         },
  23092         else => return,
  23093     }
  23094 }
  23095 
  23096 fn checkArithmeticOp(
  23097     sema: *Sema,
  23098     block: *Block,
  23099     src: LazySrcLoc,
  23100     scalar_tag: std.builtin.TypeId,
  23101     lhs_zig_ty_tag: std.builtin.TypeId,
  23102     rhs_zig_ty_tag: std.builtin.TypeId,
  23103     zir_tag: Zir.Inst.Tag,
  23104 ) CompileError!void {
  23105     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  23106     const is_float = scalar_tag == .float or scalar_tag == .comptime_float;
  23107 
  23108     if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
  23109         return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
  23110             @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
  23111         });
  23112     }
  23113 }
  23114 
  23115 fn checkPtrOperand(
  23116     sema: *Sema,
  23117     block: *Block,
  23118     ty_src: LazySrcLoc,
  23119     ty: Type,
  23120 ) CompileError!void {
  23121     const pt = sema.pt;
  23122     const zcu = pt.zcu;
  23123     switch (ty.zigTypeTag(zcu)) {
  23124         .pointer => return,
  23125         .@"fn" => {
  23126             const msg = msg: {
  23127                 const msg = try sema.errMsg(
  23128                     ty_src,
  23129                     "expected pointer, found '{f}'",
  23130                     .{ty.fmt(pt)},
  23131                 );
  23132                 errdefer msg.destroy(sema.gpa);
  23133 
  23134                 try sema.errNote(ty_src, msg, "use '&' to obtain a function pointer", .{});
  23135 
  23136                 break :msg msg;
  23137             };
  23138             return sema.failWithOwnedErrorMsg(block, msg);
  23139         },
  23140         .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return,
  23141         else => {},
  23142     }
  23143     return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)});
  23144 }
  23145 
  23146 fn checkPtrType(
  23147     sema: *Sema,
  23148     block: *Block,
  23149     ty_src: LazySrcLoc,
  23150     ty: Type,
  23151     allow_slice: bool,
  23152 ) CompileError!void {
  23153     const pt = sema.pt;
  23154     const zcu = pt.zcu;
  23155     switch (ty.zigTypeTag(zcu)) {
  23156         .pointer => if (allow_slice or !ty.isSlice(zcu)) return,
  23157         .@"fn" => {
  23158             const msg = msg: {
  23159                 const msg = try sema.errMsg(
  23160                     ty_src,
  23161                     "expected pointer type, found '{f}'",
  23162                     .{ty.fmt(pt)},
  23163                 );
  23164                 errdefer msg.destroy(sema.gpa);
  23165 
  23166                 try sema.errNote(ty_src, msg, "use '*const ' to make a function pointer type", .{});
  23167 
  23168                 break :msg msg;
  23169             };
  23170             return sema.failWithOwnedErrorMsg(block, msg);
  23171         },
  23172         .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return,
  23173         else => {},
  23174     }
  23175     return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)});
  23176 }
  23177 
  23178 fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  23179     const pt = sema.pt;
  23180     const zcu = pt.zcu;
  23181     if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) {
  23182         const target = zcu.getTarget();
  23183         const as = ty.ptrAddressSpace(zcu);
  23184         if (target_util.arePointersLogical(target, as)) {
  23185             return sema.failWithOwnedErrorMsg(block, msg: {
  23186                 const msg = try sema.errMsg(src, "illegal operation on logical pointer of type '{f}'", .{ty.fmt(pt)});
  23187                 errdefer msg.destroy(sema.gpa);
  23188                 try sema.errNote(
  23189                     src,
  23190                     msg,
  23191                     "cannot perform arithmetic on pointers with address space '{s}' on target {s}-{s}",
  23192                     .{
  23193                         @tagName(as),
  23194                         @tagName(target.cpu.arch.family()),
  23195                         @tagName(target.os.tag),
  23196                     },
  23197                 );
  23198                 break :msg msg;
  23199             });
  23200         }
  23201     }
  23202 }
  23203 
  23204 fn checkVectorElemType(
  23205     sema: *Sema,
  23206     block: *Block,
  23207     ty_src: LazySrcLoc,
  23208     ty: Type,
  23209 ) CompileError!void {
  23210     const pt = sema.pt;
  23211     const zcu = pt.zcu;
  23212     switch (ty.zigTypeTag(zcu)) {
  23213         .int, .float, .bool => return,
  23214         .optional, .pointer => if (ty.isPtrAtRuntime(zcu)) return,
  23215         else => {},
  23216     }
  23217     return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{f}'", .{ty.fmt(pt)});
  23218 }
  23219 
  23220 fn checkFloatType(
  23221     sema: *Sema,
  23222     block: *Block,
  23223     ty_src: LazySrcLoc,
  23224     ty: Type,
  23225 ) CompileError!void {
  23226     const pt = sema.pt;
  23227     const zcu = pt.zcu;
  23228     switch (ty.zigTypeTag(zcu)) {
  23229         .comptime_int, .comptime_float, .float => {},
  23230         else => return sema.fail(block, ty_src, "expected float type, found '{f}'", .{ty.fmt(pt)}),
  23231     }
  23232 }
  23233 
  23234 fn checkNumericType(
  23235     sema: *Sema,
  23236     block: *Block,
  23237     ty_src: LazySrcLoc,
  23238     ty: Type,
  23239 ) CompileError!void {
  23240     const pt = sema.pt;
  23241     const zcu = pt.zcu;
  23242     switch (ty.zigTypeTag(zcu)) {
  23243         .comptime_float, .float, .comptime_int, .int => {},
  23244         .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) {
  23245             .comptime_float, .float, .comptime_int, .int => {},
  23246             else => |t| return sema.fail(block, ty_src, "expected number, found '{t}'", .{t}),
  23247         },
  23248         else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}),
  23249     }
  23250 }
  23251 
  23252 /// Returns the casted pointer.
  23253 fn checkAtomicPtrOperand(
  23254     sema: *Sema,
  23255     block: *Block,
  23256     elem_ty: Type,
  23257     elem_ty_src: LazySrcLoc,
  23258     ptr: Air.Inst.Ref,
  23259     ptr_src: LazySrcLoc,
  23260     ptr_const: bool,
  23261 ) CompileError!Air.Inst.Ref {
  23262     const pt = sema.pt;
  23263     const zcu = pt.zcu;
  23264     var diag: Zcu.AtomicPtrAlignmentDiagnostics = .{};
  23265     const alignment = zcu.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) {
  23266         error.OutOfMemory => return error.OutOfMemory,
  23267         error.FloatTooBig => return sema.fail(
  23268             block,
  23269             elem_ty_src,
  23270             "expected {d}-bit float type or smaller; found {d}-bit float type",
  23271             .{ diag.max_bits, diag.bits },
  23272         ),
  23273         error.IntTooBig => return sema.fail(
  23274             block,
  23275             elem_ty_src,
  23276             "expected {d}-bit integer type or smaller; found {d}-bit integer type",
  23277             .{ diag.max_bits, diag.bits },
  23278         ),
  23279         error.BadType => return sema.fail(
  23280             block,
  23281             elem_ty_src,
  23282             "expected bool, integer, float, enum, packed struct, or pointer type; found '{f}'",
  23283             .{elem_ty.fmt(pt)},
  23284         ),
  23285     };
  23286 
  23287     var wanted_ptr_data: InternPool.Key.PtrType = .{
  23288         .child = elem_ty.toIntern(),
  23289         .flags = .{
  23290             .alignment = alignment,
  23291             .is_const = ptr_const,
  23292         },
  23293     };
  23294 
  23295     const ptr_ty = sema.typeOf(ptr);
  23296     const ptr_data = switch (ptr_ty.zigTypeTag(zcu)) {
  23297         .pointer => ptr_ty.ptrInfo(zcu),
  23298         else => {
  23299             const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data);
  23300             _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  23301             unreachable;
  23302         },
  23303     };
  23304 
  23305     wanted_ptr_data.flags.address_space = ptr_data.flags.address_space;
  23306     wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero;
  23307     wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile;
  23308 
  23309     const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data);
  23310     const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  23311 
  23312     return casted_ptr;
  23313 }
  23314 
  23315 fn checkPtrIsNotComptimeMutable(
  23316     sema: *Sema,
  23317     block: *Block,
  23318     ptr_val: Value,
  23319     ptr_src: LazySrcLoc,
  23320     operand_src: LazySrcLoc,
  23321 ) CompileError!void {
  23322     _ = operand_src;
  23323     if (sema.isComptimeMutablePtr(ptr_val)) {
  23324         return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{});
  23325     }
  23326 }
  23327 
  23328 fn checkIntOrVector(
  23329     sema: *Sema,
  23330     block: *Block,
  23331     operand: Air.Inst.Ref,
  23332     operand_src: LazySrcLoc,
  23333 ) CompileError!Type {
  23334     const pt = sema.pt;
  23335     const zcu = pt.zcu;
  23336     const operand_ty = sema.typeOf(operand);
  23337     switch (operand_ty.zigTypeTag(zcu)) {
  23338         .int => return operand_ty,
  23339         .vector => {
  23340             const elem_ty = operand_ty.childType(zcu);
  23341             switch (elem_ty.zigTypeTag(zcu)) {
  23342                 .int => return elem_ty,
  23343                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{
  23344                     elem_ty.fmt(pt),
  23345                 }),
  23346             }
  23347         },
  23348         else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{
  23349             operand_ty.fmt(pt),
  23350         }),
  23351     }
  23352 }
  23353 
  23354 fn checkIntOrVectorAllowComptime(
  23355     sema: *Sema,
  23356     block: *Block,
  23357     operand_ty: Type,
  23358     operand_src: LazySrcLoc,
  23359 ) CompileError!Type {
  23360     const pt = sema.pt;
  23361     const zcu = pt.zcu;
  23362     switch (operand_ty.zigTypeTag(zcu)) {
  23363         .int, .comptime_int => return operand_ty,
  23364         .vector => {
  23365             const elem_ty = operand_ty.childType(zcu);
  23366             switch (elem_ty.zigTypeTag(zcu)) {
  23367                 .int, .comptime_int => return elem_ty,
  23368                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{
  23369                     elem_ty.fmt(pt),
  23370                 }),
  23371             }
  23372         },
  23373         else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{
  23374             operand_ty.fmt(pt),
  23375         }),
  23376     }
  23377 }
  23378 
  23379 const SimdBinOp = struct {
  23380     len: ?usize,
  23381     /// Coerced to `result_ty`.
  23382     lhs: Air.Inst.Ref,
  23383     /// Coerced to `result_ty`.
  23384     rhs: Air.Inst.Ref,
  23385     lhs_val: ?Value,
  23386     rhs_val: ?Value,
  23387     /// Only different than `scalar_ty` when it is a vector operation.
  23388     result_ty: Type,
  23389     scalar_ty: Type,
  23390 };
  23391 
  23392 fn checkSimdBinOp(
  23393     sema: *Sema,
  23394     block: *Block,
  23395     src: LazySrcLoc,
  23396     uncasted_lhs: Air.Inst.Ref,
  23397     uncasted_rhs: Air.Inst.Ref,
  23398     lhs_src: LazySrcLoc,
  23399     rhs_src: LazySrcLoc,
  23400 ) CompileError!SimdBinOp {
  23401     const pt = sema.pt;
  23402     const zcu = pt.zcu;
  23403     const lhs_ty = sema.typeOf(uncasted_lhs);
  23404     const rhs_ty = sema.typeOf(uncasted_rhs);
  23405 
  23406     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  23407     const vec_len: ?usize = if (lhs_ty.zigTypeTag(zcu) == .vector) lhs_ty.vectorLen(zcu) else null;
  23408     const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{
  23409         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  23410     });
  23411     const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src);
  23412     const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src);
  23413 
  23414     return SimdBinOp{
  23415         .len = vec_len,
  23416         .lhs = lhs,
  23417         .rhs = rhs,
  23418         .lhs_val = try sema.resolveValue(lhs),
  23419         .rhs_val = try sema.resolveValue(rhs),
  23420         .result_ty = result_ty,
  23421         .scalar_ty = result_ty.scalarType(zcu),
  23422     };
  23423 }
  23424 
  23425 fn checkVectorizableBinaryOperands(
  23426     sema: *Sema,
  23427     block: *Block,
  23428     src: LazySrcLoc,
  23429     lhs_ty: Type,
  23430     rhs_ty: Type,
  23431     lhs_src: LazySrcLoc,
  23432     rhs_src: LazySrcLoc,
  23433 ) CompileError!void {
  23434     const pt = sema.pt;
  23435     const zcu = pt.zcu;
  23436     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  23437     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  23438     if (lhs_zig_ty_tag != .vector and rhs_zig_ty_tag != .vector) return;
  23439 
  23440     const lhs_is_vector = switch (lhs_zig_ty_tag) {
  23441         .vector, .array => true,
  23442         else => false,
  23443     };
  23444     const rhs_is_vector = switch (rhs_zig_ty_tag) {
  23445         .vector, .array => true,
  23446         else => false,
  23447     };
  23448 
  23449     if (lhs_is_vector and rhs_is_vector) {
  23450         const lhs_len = lhs_ty.arrayLen(zcu);
  23451         const rhs_len = rhs_ty.arrayLen(zcu);
  23452         if (lhs_len != rhs_len) {
  23453             const msg = msg: {
  23454                 const msg = try sema.errMsg(src, "vector length mismatch", .{});
  23455                 errdefer msg.destroy(sema.gpa);
  23456                 try sema.errNote(lhs_src, msg, "length {d} here", .{lhs_len});
  23457                 try sema.errNote(rhs_src, msg, "length {d} here", .{rhs_len});
  23458                 break :msg msg;
  23459             };
  23460             return sema.failWithOwnedErrorMsg(block, msg);
  23461         }
  23462     } else {
  23463         const msg = msg: {
  23464             const msg = try sema.errMsg(src, "mixed scalar and vector operands: '{f}' and '{f}'", .{
  23465                 lhs_ty.fmt(pt), rhs_ty.fmt(pt),
  23466             });
  23467             errdefer msg.destroy(sema.gpa);
  23468             if (lhs_is_vector) {
  23469                 try sema.errNote(lhs_src, msg, "vector here", .{});
  23470                 try sema.errNote(rhs_src, msg, "scalar here", .{});
  23471             } else {
  23472                 try sema.errNote(lhs_src, msg, "scalar here", .{});
  23473                 try sema.errNote(rhs_src, msg, "vector here", .{});
  23474             }
  23475             break :msg msg;
  23476         };
  23477         return sema.failWithOwnedErrorMsg(block, msg);
  23478     }
  23479 }
  23480 
  23481 fn checkAllScalarsDefined(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) CompileError!void {
  23482     const zcu = sema.pt.zcu;
  23483     switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  23484         .int, .float => {},
  23485         .undef => return sema.failWithUseOfUndef(block, src, null),
  23486         .aggregate => |agg| {
  23487             assert(Type.fromInterned(agg.ty).zigTypeTag(zcu) == .vector);
  23488             for (agg.storage.values(), 0..) |elem_val, elem_idx| {
  23489                 if (Value.fromInterned(elem_val).isUndef(zcu))
  23490                     return sema.failWithUseOfUndef(block, src, elem_idx);
  23491             }
  23492         },
  23493         else => unreachable,
  23494     }
  23495 }
  23496 
  23497 fn resolveExportOptions(
  23498     sema: *Sema,
  23499     block: *Block,
  23500     src: LazySrcLoc,
  23501     zir_ref: Zir.Inst.Ref,
  23502 ) CompileError!Zcu.Export.Options {
  23503     const pt = sema.pt;
  23504     const zcu = pt.zcu;
  23505     const gpa = sema.gpa;
  23506     const ip = &zcu.intern_pool;
  23507     const export_options_ty = try sema.getBuiltinType(src, .ExportOptions);
  23508     const air_ref = try sema.resolveInst(zir_ref);
  23509     const options = try sema.coerce(block, export_options_ty, air_ref, src);
  23510 
  23511     const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  23512     const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  23513     const section_src = block.src(.{ .init_field_section = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  23514     const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  23515 
  23516     const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
  23517     const name = try sema.toConstString(block, name_src, name_operand, .{ .simple = .export_options });
  23518 
  23519     const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
  23520     const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ .simple = .export_options });
  23521     const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage);
  23522 
  23523     const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "section", .no_embedded_nulls), section_src);
  23524     const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ .simple = .export_options });
  23525     const section = if (section_opt_val.optionalValue(zcu)) |section_val|
  23526         try sema.toConstString(block, section_src, Air.internedToRef(section_val.toIntern()), .{ .simple = .export_options })
  23527     else
  23528         null;
  23529 
  23530     const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
  23531     const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{ .simple = .export_options });
  23532     const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility);
  23533 
  23534     if (name.len < 1) {
  23535         return sema.fail(block, name_src, "exported symbol name cannot be empty", .{});
  23536     }
  23537 
  23538     if (visibility != .default and linkage == .internal) {
  23539         return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{
  23540             name, @tagName(visibility),
  23541         });
  23542     }
  23543 
  23544     return .{
  23545         .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls),
  23546         .linkage = linkage,
  23547         .section = try ip.getOrPutStringOpt(gpa, pt.tid, section, .no_embedded_nulls),
  23548         .visibility = visibility,
  23549     };
  23550 }
  23551 
  23552 fn resolveBuiltinEnum(
  23553     sema: *Sema,
  23554     block: *Block,
  23555     src: LazySrcLoc,
  23556     zir_ref: Zir.Inst.Ref,
  23557     comptime name: Zcu.BuiltinDecl,
  23558     reason: ComptimeReason,
  23559 ) CompileError!@field(std.builtin, @tagName(name)) {
  23560     const ty = try sema.getBuiltinType(src, name);
  23561     const air_ref = try sema.resolveInst(zir_ref);
  23562     const coerced = try sema.coerce(block, ty, air_ref, src);
  23563     const val = try sema.resolveConstDefinedValue(block, src, coerced, reason);
  23564     return sema.interpretBuiltinType(block, src, val, @field(std.builtin, @tagName(name)));
  23565 }
  23566 
  23567 fn resolveAtomicOrder(
  23568     sema: *Sema,
  23569     block: *Block,
  23570     src: LazySrcLoc,
  23571     zir_ref: Zir.Inst.Ref,
  23572     reason: ComptimeReason,
  23573 ) CompileError!std.builtin.AtomicOrder {
  23574     return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicOrder, reason);
  23575 }
  23576 
  23577 fn resolveAtomicRmwOp(
  23578     sema: *Sema,
  23579     block: *Block,
  23580     src: LazySrcLoc,
  23581     zir_ref: Zir.Inst.Ref,
  23582 ) CompileError!std.builtin.AtomicRmwOp {
  23583     return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicRmwOp, .{ .simple = .operand_atomicRmw_operation });
  23584 }
  23585 
  23586 fn zirCmpxchg(
  23587     sema: *Sema,
  23588     block: *Block,
  23589     extended: Zir.Inst.Extended.InstData,
  23590 ) CompileError!Air.Inst.Ref {
  23591     const pt = sema.pt;
  23592     const zcu = pt.zcu;
  23593     const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data;
  23594     const air_tag: Air.Inst.Tag = switch (extended.small) {
  23595         0 => .cmpxchg_weak,
  23596         1 => .cmpxchg_strong,
  23597         else => unreachable,
  23598     };
  23599     const src = block.nodeOffset(extra.node);
  23600     // zig fmt: off
  23601     const elem_ty_src       = block.builtinCallArgSrc(extra.node, 0);
  23602     const ptr_src           = block.builtinCallArgSrc(extra.node, 1);
  23603     const expected_src      = block.builtinCallArgSrc(extra.node, 2);
  23604     const new_value_src     = block.builtinCallArgSrc(extra.node, 3);
  23605     const success_order_src = block.builtinCallArgSrc(extra.node, 4);
  23606     const failure_order_src = block.builtinCallArgSrc(extra.node, 5);
  23607     // zig fmt: on
  23608     const expected_value = try sema.resolveInst(extra.expected_value);
  23609     const elem_ty = sema.typeOf(expected_value);
  23610     if (elem_ty.zigTypeTag(zcu) == .float) {
  23611         return sema.fail(
  23612             block,
  23613             elem_ty_src,
  23614             "expected bool, integer, enum, packed struct, or pointer type; found '{f}'",
  23615             .{elem_ty.fmt(pt)},
  23616         );
  23617     }
  23618     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  23619     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  23620     const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src);
  23621     const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, .{ .simple = .atomic_order });
  23622     const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, .{ .simple = .atomic_order });
  23623 
  23624     if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.monotonic)) {
  23625         return sema.fail(block, success_order_src, "success atomic ordering must be monotonic or stricter", .{});
  23626     }
  23627     if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.monotonic)) {
  23628         return sema.fail(block, failure_order_src, "failure atomic ordering must be monotonic or stricter", .{});
  23629     }
  23630     if (@intFromEnum(failure_order) > @intFromEnum(success_order)) {
  23631         return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{});
  23632     }
  23633     if (failure_order == .release or failure_order == .acq_rel) {
  23634         return sema.fail(block, failure_order_src, "failure atomic ordering must not be release or acq_rel", .{});
  23635     }
  23636 
  23637     const result_ty = try pt.optionalType(elem_ty.toIntern());
  23638 
  23639     // special case zero bit types
  23640     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
  23641         return Air.internedToRef((try pt.intern(.{ .opt = .{
  23642             .ty = result_ty.toIntern(),
  23643             .val = .none,
  23644         } })));
  23645     }
  23646 
  23647     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  23648         if (try sema.resolveValue(expected_value)) |expected_val| {
  23649             if (try sema.resolveValue(new_value)) |new_val| {
  23650                 if (expected_val.isUndef(zcu) or new_val.isUndef(zcu)) {
  23651                     // TODO: this should probably cause the memory stored at the pointer
  23652                     // to become undef as well
  23653                     return pt.undefRef(result_ty);
  23654                 }
  23655                 const ptr_ty = sema.typeOf(ptr);
  23656                 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  23657                 const result_val = try pt.intern(.{ .opt = .{
  23658                     .ty = result_ty.toIntern(),
  23659                     .val = if (stored_val.eql(expected_val, elem_ty, zcu)) blk: {
  23660                         try sema.storePtr(block, src, ptr, new_value);
  23661                         break :blk .none;
  23662                     } else stored_val.toIntern(),
  23663                 } });
  23664                 return Air.internedToRef(result_val);
  23665             } else break :rs new_value_src;
  23666         } else break :rs expected_src;
  23667     } else ptr_src;
  23668 
  23669     const flags: u32 = @as(u32, @intFromEnum(success_order)) |
  23670         (@as(u32, @intFromEnum(failure_order)) << 3);
  23671 
  23672     try sema.requireRuntimeBlock(block, src, runtime_src);
  23673     return block.addInst(.{
  23674         .tag = air_tag,
  23675         .data = .{ .ty_pl = .{
  23676             .ty = Air.internedToRef(result_ty.toIntern()),
  23677             .payload = try sema.addExtra(Air.Cmpxchg{
  23678                 .ptr = ptr,
  23679                 .expected_value = expected_value,
  23680                 .new_value = new_value,
  23681                 .flags = flags,
  23682             }),
  23683         } },
  23684     });
  23685 }
  23686 
  23687 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23688     const pt = sema.pt;
  23689     const zcu = pt.zcu;
  23690     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23691     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23692     const src = block.nodeOffset(inst_data.src_node);
  23693     const scalar_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  23694     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat");
  23695 
  23696     switch (dest_ty.zigTypeTag(zcu)) {
  23697         .array, .vector => {},
  23698         else => return sema.fail(block, src, "expected array or vector type, found '{f}'", .{dest_ty.fmt(pt)}),
  23699     }
  23700 
  23701     const operand = try sema.resolveInst(extra.rhs);
  23702     const scalar_ty = dest_ty.childType(zcu);
  23703     const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src);
  23704 
  23705     const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu));
  23706 
  23707     if (try sema.typeHasOnePossibleValue(dest_ty)) |val| {
  23708         return Air.internedToRef(val.toIntern());
  23709     }
  23710 
  23711     // We also need this case because `[0:s]T` is not OPV.
  23712     if (len == 0) return .fromValue(try pt.aggregateValue(dest_ty, &.{}));
  23713 
  23714     const maybe_sentinel = dest_ty.sentinel(zcu);
  23715 
  23716     if (try sema.resolveValue(scalar)) |scalar_val| {
  23717         full: {
  23718             if (dest_ty.zigTypeTag(zcu) == .vector) break :full;
  23719             const sentinel = maybe_sentinel orelse break :full;
  23720             if (sentinel.toIntern() == scalar_val.toIntern()) break :full;
  23721             // This is a array with non-zero length and a sentinel which does not match the element.
  23722             // We have to use the full `elems` representation.
  23723             const elems = try sema.arena.alloc(InternPool.Index, len + 1);
  23724             @memset(elems[0..len], scalar_val.toIntern());
  23725             elems[len] = sentinel.toIntern();
  23726             return .fromValue(try pt.aggregateValue(dest_ty, elems));
  23727         }
  23728         return .fromValue(try pt.aggregateSplatValue(dest_ty, scalar_val));
  23729     }
  23730 
  23731     try sema.requireRuntimeBlock(block, src, scalar_src);
  23732 
  23733     switch (dest_ty.zigTypeTag(zcu)) {
  23734         .array => {
  23735             const elems = try sema.arena.alloc(Air.Inst.Ref, len + @intFromBool(maybe_sentinel != null));
  23736             @memset(elems[0..len], scalar);
  23737             if (maybe_sentinel) |s| elems[len] = Air.internedToRef(s.toIntern());
  23738             return block.addAggregateInit(dest_ty, elems);
  23739         },
  23740         .vector => return block.addTyOp(.splat, dest_ty, scalar),
  23741         else => unreachable,
  23742     }
  23743 }
  23744 
  23745 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23746     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23747     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23748     const op_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  23749     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  23750     const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, .ReduceOp, .{ .simple = .operand_reduce_operation });
  23751     const operand = try sema.resolveInst(extra.rhs);
  23752     const operand_ty = sema.typeOf(operand);
  23753     const pt = sema.pt;
  23754     const zcu = pt.zcu;
  23755 
  23756     if (operand_ty.zigTypeTag(zcu) != .vector) {
  23757         return sema.fail(block, operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)});
  23758     }
  23759 
  23760     const scalar_ty = operand_ty.childType(zcu);
  23761 
  23762     // Type-check depending on operation.
  23763     switch (operation) {
  23764         .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) {
  23765             .int, .bool => {},
  23766             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{f}'", .{
  23767                 @tagName(operation), operand_ty.fmt(pt),
  23768             }),
  23769         },
  23770         .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(zcu)) {
  23771             .int, .float => {},
  23772             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{f}'", .{
  23773                 @tagName(operation), operand_ty.fmt(pt),
  23774             }),
  23775         },
  23776     }
  23777 
  23778     const vec_len = operand_ty.vectorLen(zcu);
  23779     if (vec_len == 0) {
  23780         // TODO re-evaluate if we should introduce a "neutral value" for some operations,
  23781         // e.g. zero for add and one for mul.
  23782         return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{});
  23783     }
  23784 
  23785     if (try sema.resolveValue(operand)) |operand_val| {
  23786         if (operand_val.isUndef(zcu)) return pt.undefRef(scalar_ty);
  23787 
  23788         var accum: Value = try operand_val.elemValue(pt, 0);
  23789         var i: u32 = 1;
  23790         while (i < vec_len) : (i += 1) {
  23791             const elem_val = try operand_val.elemValue(pt, i);
  23792             accum = switch (operation) {
  23793                 // zig fmt: off
  23794                 .And => try arith.bitwiseBin  (sema, scalar_ty, accum, elem_val, .@"and"),
  23795                 .Or  => try arith.bitwiseBin  (sema, scalar_ty, accum, elem_val, .@"or"),
  23796                 .Xor => try arith.bitwiseBin  (sema, scalar_ty, accum, elem_val, .xor),
  23797                 .Min => Value.numberMin       (                 accum, elem_val, zcu),
  23798                 .Max => Value.numberMax       (                 accum, elem_val, zcu),
  23799                 .Add => try arith.addMaybeWrap(sema, scalar_ty, accum, elem_val),
  23800                 .Mul => try arith.mulMaybeWrap(sema, scalar_ty, accum, elem_val),
  23801                 // zig fmt: on
  23802             };
  23803         }
  23804         return Air.internedToRef(accum.toIntern());
  23805     }
  23806 
  23807     try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src);
  23808     return block.addReduce(operand, operation);
  23809 }
  23810 
  23811 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23812     const pt = sema.pt;
  23813     const zcu = pt.zcu;
  23814     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23815     const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
  23816     const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  23817     const mask_src = block.builtinCallArgSrc(inst_data.src_node, 3);
  23818 
  23819     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  23820     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  23821     const a = try sema.resolveInst(extra.a);
  23822     const b = try sema.resolveInst(extra.b);
  23823     var mask = try sema.resolveInst(extra.mask);
  23824     var mask_ty = sema.typeOf(mask);
  23825 
  23826     const mask_len = switch (sema.typeOf(mask).zigTypeTag(zcu)) {
  23827         .array, .vector => sema.typeOf(mask).arrayLen(zcu),
  23828         else => return sema.fail(block, mask_src, "expected vector or array, found '{f}'", .{sema.typeOf(mask).fmt(pt)}),
  23829     };
  23830     mask_ty = try pt.vectorType(.{
  23831         .len = @intCast(mask_len),
  23832         .child = .i32_type,
  23833     });
  23834     mask = try sema.coerce(block, mask_ty, mask, mask_src);
  23835     const mask_val = try sema.resolveConstValue(block, mask_src, mask, .{ .simple = .operand_shuffle_mask });
  23836     return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(mask_len));
  23837 }
  23838 
  23839 fn analyzeShuffle(
  23840     sema: *Sema,
  23841     block: *Block,
  23842     src_node: std.zig.Ast.Node.Offset,
  23843     elem_ty: Type,
  23844     a_uncoerced: Air.Inst.Ref,
  23845     b_uncoerced: Air.Inst.Ref,
  23846     mask: Value,
  23847     mask_len: u32,
  23848 ) CompileError!Air.Inst.Ref {
  23849     const pt = sema.pt;
  23850     const zcu = pt.zcu;
  23851     const a_src = block.builtinCallArgSrc(src_node, 1);
  23852     const b_src = block.builtinCallArgSrc(src_node, 2);
  23853     const mask_src = block.builtinCallArgSrc(src_node, 3);
  23854 
  23855     // If the type of `a` is `@Type(.undefined)`, i.e. the argument is untyped,
  23856     // this is 0, because it is an error to index into this vector.
  23857     const a_len: u32 = switch (sema.typeOf(a_uncoerced).zigTypeTag(zcu)) {
  23858         .array, .vector => @intCast(sema.typeOf(a_uncoerced).arrayLen(zcu)),
  23859         .undefined => 0,
  23860         else => return sema.fail(block, a_src, "expected vector of '{f}', found '{f}'", .{
  23861             elem_ty.fmt(pt), sema.typeOf(a_uncoerced).fmt(pt),
  23862         }),
  23863     };
  23864     const a_ty = try pt.vectorType(.{ .len = a_len, .child = elem_ty.toIntern() });
  23865     const a_coerced = try sema.coerce(block, a_ty, a_uncoerced, a_src);
  23866 
  23867     // If the type of `b` is `@Type(.undefined)`, i.e. the argument is untyped, this is 0, because it is an error to index into this vector.
  23868     const b_len: u32 = switch (sema.typeOf(b_uncoerced).zigTypeTag(zcu)) {
  23869         .array, .vector => @intCast(sema.typeOf(b_uncoerced).arrayLen(zcu)),
  23870         .undefined => 0,
  23871         else => return sema.fail(block, b_src, "expected vector of '{f}', found '{f}'", .{
  23872             elem_ty.fmt(pt), sema.typeOf(b_uncoerced).fmt(pt),
  23873         }),
  23874     };
  23875     const b_ty = try pt.vectorType(.{ .len = b_len, .child = elem_ty.toIntern() });
  23876     const b_coerced = try sema.coerce(block, b_ty, b_uncoerced, b_src);
  23877 
  23878     const result_ty = try pt.vectorType(.{ .len = mask_len, .child = elem_ty.toIntern() });
  23879 
  23880     // We're going to pre-emptively reserve space in `sema.air_extra`. The reason for this is we need
  23881     // a `u32` buffer of length `mask_len` anyway, and putting it in `sema.air_extra` avoids a copy
  23882     // in the runtime case. If the result is comptime-known, we'll shrink `air_extra` back.
  23883     const air_extra_idx: u32 = @intCast(sema.air_extra.items.len);
  23884     const air_mask_buf = try sema.air_extra.addManyAsSlice(sema.gpa, mask_len);
  23885 
  23886     // We want to interpret that buffer in `air_extra` in a few ways. Initially, we'll consider its
  23887     // elements as `Air.Inst.ShuffleTwoMask`, essentially representing the raw mask values; then, we'll
  23888     // convert it to `InternPool.Index` or `Air.Inst.ShuffleOneMask` if there are comptime-known operands.
  23889     const mask_ip_index: []InternPool.Index = @ptrCast(air_mask_buf);
  23890     const mask_shuffle_one: []Air.ShuffleOneMask = @ptrCast(air_mask_buf);
  23891     const mask_shuffle_two: []Air.ShuffleTwoMask = @ptrCast(air_mask_buf);
  23892 
  23893     // Initial loop: check mask elements, populate `mask_shuffle_two`.
  23894     var a_used = false;
  23895     var b_used = false;
  23896     for (mask_shuffle_two, 0..mask_len) |*out, mask_idx| {
  23897         const mask_val = try mask.elemValue(pt, mask_idx);
  23898         if (mask_val.isUndef(zcu)) {
  23899             out.* = .undef;
  23900             continue;
  23901         }
  23902         // Safe because mask elements are `i32` and we already checked for undef:
  23903         const raw = (try sema.resolveLazyValue(mask_val)).toSignedInt(zcu);
  23904         if (raw >= 0) {
  23905             const idx: u32 = @intCast(raw);
  23906             a_used = true;
  23907             out.* = .aElem(idx);
  23908             if (idx >= a_len) return sema.failWithOwnedErrorMsg(block, msg: {
  23909                 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx});
  23910                 errdefer msg.destroy(sema.gpa);
  23911                 try sema.errNote(a_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, a_ty.fmt(pt) });
  23912                 if (idx < b_len) {
  23913                     try sema.errNote(b_src, msg, "use '~@as(u32, {d})' to index into second vector given here", .{idx});
  23914                 }
  23915                 break :msg msg;
  23916             });
  23917         } else {
  23918             const idx: u32 = @intCast(~raw);
  23919             b_used = true;
  23920             out.* = .bElem(idx);
  23921             if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: {
  23922                 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx});
  23923                 errdefer msg.destroy(sema.gpa);
  23924                 try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, b_ty.fmt(pt) });
  23925                 break :msg msg;
  23926             });
  23927         }
  23928     }
  23929 
  23930     const maybe_a_val = try sema.resolveValue(a_coerced);
  23931     const maybe_b_val = try sema.resolveValue(b_coerced);
  23932 
  23933     const a_rt = a_used and maybe_a_val == null;
  23934     const b_rt = b_used and maybe_b_val == null;
  23935 
  23936     if (a_rt and b_rt) {
  23937         // Both operands are needed and runtime-known. We need a `[]ShuffleTwomask`... which is
  23938         // exactly what we already have in `mask_shuffle_two`! So, we're basically done already.
  23939         // We just need to append the two operands.
  23940         try sema.air_extra.ensureUnusedCapacity(sema.gpa, 2);
  23941         sema.appendRefsAssumeCapacity(&.{ a_coerced, b_coerced });
  23942         return block.addInst(.{
  23943             .tag = .shuffle_two,
  23944             .data = .{ .ty_pl = .{
  23945                 .ty = Air.internedToRef(result_ty.toIntern()),
  23946                 .payload = air_extra_idx,
  23947             } },
  23948         });
  23949     } else if (a_rt) {
  23950         // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`.
  23951         for (mask_shuffle_two, mask_shuffle_one) |in, *out| {
  23952             out.* = switch (in.unwrap()) {
  23953                 .undef => .value(try pt.undefValue(elem_ty)),
  23954                 .a_elem => |idx| .elem(idx),
  23955                 .b_elem => |idx| .value(try maybe_b_val.?.elemValue(pt, idx)),
  23956             };
  23957         }
  23958         // Now just append our single runtime operand, and we're done.
  23959         try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1);
  23960         sema.appendRefsAssumeCapacity(&.{a_coerced});
  23961         return block.addInst(.{
  23962             .tag = .shuffle_one,
  23963             .data = .{ .ty_pl = .{
  23964                 .ty = Air.internedToRef(result_ty.toIntern()),
  23965                 .payload = air_extra_idx,
  23966             } },
  23967         });
  23968     } else if (b_rt) {
  23969         // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`.
  23970         for (mask_shuffle_two, mask_shuffle_one) |in, *out| {
  23971             out.* = switch (in.unwrap()) {
  23972                 .undef => .value(try pt.undefValue(elem_ty)),
  23973                 .a_elem => |idx| .value(try maybe_a_val.?.elemValue(pt, idx)),
  23974                 .b_elem => |idx| .elem(idx),
  23975             };
  23976         }
  23977         // Now just append our single runtime operand, and we're done.
  23978         try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1);
  23979         sema.appendRefsAssumeCapacity(&.{b_coerced});
  23980         return block.addInst(.{
  23981             .tag = .shuffle_one,
  23982             .data = .{ .ty_pl = .{
  23983                 .ty = Air.internedToRef(result_ty.toIntern()),
  23984                 .payload = air_extra_idx,
  23985             } },
  23986         });
  23987     } else {
  23988         // The result will be comptime-known. We must convert the `ShuffleTwoMask` values to
  23989         // `InternPool.Index` values using the known operands.
  23990         for (mask_shuffle_two, mask_ip_index) |in, *out| {
  23991             const val: Value = switch (in.unwrap()) {
  23992                 .undef => try pt.undefValue(elem_ty),
  23993                 .a_elem => |idx| try maybe_a_val.?.elemValue(pt, idx),
  23994                 .b_elem => |idx| try maybe_b_val.?.elemValue(pt, idx),
  23995             };
  23996             out.* = val.toIntern();
  23997         }
  23998         const res = try pt.aggregateValue(result_ty, mask_ip_index);
  23999         // We have a comptime-known result, so didn't need `air_mask_buf` -- remove it from `sema.air_extra`.
  24000         assert(sema.air_extra.items.len == air_extra_idx + air_mask_buf.len);
  24001         sema.air_extra.shrinkRetainingCapacity(air_extra_idx);
  24002         return Air.internedToRef(res.toIntern());
  24003     }
  24004 }
  24005 
  24006 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  24007     const pt = sema.pt;
  24008     const zcu = pt.zcu;
  24009     const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data;
  24010 
  24011     const src = block.nodeOffset(extra.node);
  24012     const elem_ty_src = block.builtinCallArgSrc(extra.node, 0);
  24013     const pred_src = block.builtinCallArgSrc(extra.node, 1);
  24014     const a_src = block.builtinCallArgSrc(extra.node, 2);
  24015     const b_src = block.builtinCallArgSrc(extra.node, 3);
  24016 
  24017     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  24018     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  24019     const pred_uncoerced = try sema.resolveInst(extra.pred);
  24020     const pred_ty = sema.typeOf(pred_uncoerced);
  24021 
  24022     const vec_len_u64 = switch (pred_ty.zigTypeTag(zcu)) {
  24023         .vector, .array => pred_ty.arrayLen(zcu),
  24024         else => return sema.fail(block, pred_src, "expected vector or array, found '{f}'", .{pred_ty.fmt(pt)}),
  24025     };
  24026     const vec_len: u32 = @intCast(try sema.usizeCast(block, pred_src, vec_len_u64));
  24027 
  24028     const bool_vec_ty = try pt.vectorType(.{
  24029         .len = vec_len,
  24030         .child = .bool_type,
  24031     });
  24032     const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src);
  24033 
  24034     const vec_ty = try pt.vectorType(.{
  24035         .len = vec_len,
  24036         .child = elem_ty.toIntern(),
  24037     });
  24038     const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src);
  24039     const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src);
  24040 
  24041     const maybe_pred = try sema.resolveValue(pred);
  24042     const maybe_a = try sema.resolveValue(a);
  24043     const maybe_b = try sema.resolveValue(b);
  24044 
  24045     const runtime_src = if (maybe_pred) |pred_val| rs: {
  24046         if (pred_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  24047 
  24048         if (maybe_a) |a_val| {
  24049             if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  24050 
  24051             if (maybe_b) |b_val| {
  24052                 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  24053 
  24054                 const elems = try sema.gpa.alloc(InternPool.Index, vec_len);
  24055                 defer sema.gpa.free(elems);
  24056                 for (elems, 0..) |*elem, i| {
  24057                     const pred_elem_val = try pred_val.elemValue(pt, i);
  24058                     const should_choose_a = pred_elem_val.toBool();
  24059                     elem.* = (try (if (should_choose_a) a_val else b_val).elemValue(pt, i)).toIntern();
  24060                 }
  24061 
  24062                 return Air.internedToRef((try pt.aggregateValue(vec_ty, elems)).toIntern());
  24063             } else {
  24064                 break :rs b_src;
  24065             }
  24066         } else {
  24067             if (maybe_b) |b_val| {
  24068                 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  24069             }
  24070             break :rs a_src;
  24071         }
  24072     } else rs: {
  24073         if (maybe_a) |a_val| {
  24074             if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  24075         }
  24076         if (maybe_b) |b_val| {
  24077             if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  24078         }
  24079         break :rs pred_src;
  24080     };
  24081 
  24082     try sema.requireRuntimeBlock(block, src, runtime_src);
  24083     return block.addInst(.{
  24084         .tag = .select,
  24085         .data = .{ .pl_op = .{
  24086             .operand = pred,
  24087             .payload = try block.sema.addExtra(Air.Bin{
  24088                 .lhs = a,
  24089                 .rhs = b,
  24090             }),
  24091         } },
  24092     });
  24093 }
  24094 
  24095 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  24096     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24097     const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
  24098     // zig fmt: off
  24099     const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  24100     const ptr_src     = block.builtinCallArgSrc(inst_data.src_node, 1);
  24101     const order_src   = block.builtinCallArgSrc(inst_data.src_node, 2);
  24102     // zig fmt: on
  24103     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  24104     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  24105     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
  24106     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
  24107 
  24108     switch (order) {
  24109         .release, .acq_rel => {
  24110             return sema.fail(
  24111                 block,
  24112                 order_src,
  24113                 "@atomicLoad atomic ordering must not be release or acq_rel",
  24114                 .{},
  24115             );
  24116         },
  24117         else => {},
  24118     }
  24119 
  24120     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  24121         return Air.internedToRef(val.toIntern());
  24122     }
  24123 
  24124     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  24125         if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| {
  24126             return Air.internedToRef(elem_val.toIntern());
  24127         }
  24128     }
  24129 
  24130     try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src);
  24131     return block.addInst(.{
  24132         .tag = .atomic_load,
  24133         .data = .{ .atomic_load = .{
  24134             .ptr = ptr,
  24135             .order = order,
  24136         } },
  24137     });
  24138 }
  24139 
  24140 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  24141     const pt = sema.pt;
  24142     const zcu = pt.zcu;
  24143     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24144     const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
  24145     const src = block.nodeOffset(inst_data.src_node);
  24146     // zig fmt: off
  24147     const elem_ty_src    = block.builtinCallArgSrc(inst_data.src_node, 0);
  24148     const ptr_src        = block.builtinCallArgSrc(inst_data.src_node, 1);
  24149     const op_src         = block.builtinCallArgSrc(inst_data.src_node, 2);
  24150     const operand_src    = block.builtinCallArgSrc(inst_data.src_node, 3);
  24151     const order_src      = block.builtinCallArgSrc(inst_data.src_node, 4);
  24152     // zig fmt: on
  24153     const operand = try sema.resolveInst(extra.operand);
  24154     const elem_ty = sema.typeOf(operand);
  24155     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  24156     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  24157     const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);
  24158 
  24159     switch (elem_ty.zigTypeTag(zcu)) {
  24160         .@"enum" => if (op != .Xchg) {
  24161             return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{});
  24162         },
  24163         .bool => if (op != .Xchg) {
  24164             return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{});
  24165         },
  24166         .float => switch (op) {
  24167             .Xchg, .Add, .Sub, .Max, .Min => {},
  24168             else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}),
  24169         },
  24170         else => {},
  24171     }
  24172     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
  24173 
  24174     if (order == .unordered) {
  24175         return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be unordered", .{});
  24176     }
  24177 
  24178     // special case zero bit types
  24179     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  24180         return Air.internedToRef(val.toIntern());
  24181     }
  24182 
  24183     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  24184         const maybe_operand_val = try sema.resolveValue(operand);
  24185         const operand_val = maybe_operand_val orelse {
  24186             try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
  24187             break :rs operand_src;
  24188         };
  24189         if (sema.isComptimeMutablePtr(ptr_val)) {
  24190             const ptr_ty = sema.typeOf(ptr);
  24191             const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  24192             const new_val = switch (op) {
  24193                 // zig fmt: off
  24194                 .Xchg => operand_val,
  24195                 .Add  => try arith.addMaybeWrap(sema, elem_ty, stored_val, operand_val),
  24196                 .Sub  => try arith.subMaybeWrap(sema, elem_ty, stored_val, operand_val),
  24197                 .And  => try arith.bitwiseBin  (sema, elem_ty, stored_val, operand_val, .@"and"),
  24198                 .Nand => try arith.bitwiseBin  (sema, elem_ty, stored_val, operand_val, .nand),
  24199                 .Or   => try arith.bitwiseBin  (sema, elem_ty, stored_val, operand_val, .@"or"),
  24200                 .Xor  => try arith.bitwiseBin  (sema, elem_ty, stored_val, operand_val, .xor),
  24201                 .Max  => Value.numberMax       (               stored_val, operand_val, zcu),
  24202                 .Min  => Value.numberMin       (               stored_val, operand_val, zcu),
  24203                 // zig fmt: on
  24204             };
  24205             try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty);
  24206             return Air.internedToRef(stored_val.toIntern());
  24207         } else break :rs ptr_src;
  24208     } else ptr_src;
  24209 
  24210     const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3);
  24211 
  24212     try sema.requireRuntimeBlock(block, src, runtime_src);
  24213     return block.addInst(.{
  24214         .tag = .atomic_rmw,
  24215         .data = .{ .pl_op = .{
  24216             .operand = ptr,
  24217             .payload = try sema.addExtra(Air.AtomicRmw{
  24218                 .operand = operand,
  24219                 .flags = flags,
  24220             }),
  24221         } },
  24222     });
  24223 }
  24224 
  24225 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  24226     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24227     const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
  24228     const src = block.nodeOffset(inst_data.src_node);
  24229     // zig fmt: off
  24230     const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  24231     const ptr_src     = block.builtinCallArgSrc(inst_data.src_node, 1);
  24232     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 2);
  24233     const order_src   = block.builtinCallArgSrc(inst_data.src_node, 3);
  24234     // zig fmt: on
  24235     const operand = try sema.resolveInst(extra.operand);
  24236     const elem_ty = sema.typeOf(operand);
  24237     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  24238     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  24239     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
  24240 
  24241     const air_tag: Air.Inst.Tag = switch (order) {
  24242         .acquire, .acq_rel => {
  24243             return sema.fail(
  24244                 block,
  24245                 order_src,
  24246                 "@atomicStore atomic ordering must not be acquire or acq_rel",
  24247                 .{},
  24248             );
  24249         },
  24250         .unordered => .atomic_store_unordered,
  24251         .monotonic => .atomic_store_monotonic,
  24252         .release => .atomic_store_release,
  24253         .seq_cst => .atomic_store_seq_cst,
  24254     };
  24255 
  24256     return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
  24257 }
  24258 
  24259 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  24260     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24261     const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
  24262     const src = block.nodeOffset(inst_data.src_node);
  24263 
  24264     const mulend1_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  24265     const mulend2_src = block.builtinCallArgSrc(inst_data.src_node, 2);
  24266     const addend_src = block.builtinCallArgSrc(inst_data.src_node, 3);
  24267 
  24268     const addend = try sema.resolveInst(extra.addend);
  24269     const ty = sema.typeOf(addend);
  24270     const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src);
  24271     const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src);
  24272 
  24273     const maybe_mulend1 = try sema.resolveValue(mulend1);
  24274     const maybe_mulend2 = try sema.resolveValue(mulend2);
  24275     const maybe_addend = try sema.resolveValue(addend);
  24276     const pt = sema.pt;
  24277     const zcu = pt.zcu;
  24278 
  24279     switch (ty.scalarType(zcu).zigTypeTag(zcu)) {
  24280         .comptime_float, .float => {},
  24281         else => return sema.fail(block, src, "expected vector of floats or float type, found '{f}'", .{ty.fmt(pt)}),
  24282     }
  24283 
  24284     const runtime_src = if (maybe_mulend1) |mulend1_val| rs: {
  24285         if (maybe_mulend2) |mulend2_val| {
  24286             if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty);
  24287 
  24288             if (maybe_addend) |addend_val| {
  24289                 if (addend_val.isUndef(zcu)) return pt.undefRef(ty);
  24290                 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, pt);
  24291                 return Air.internedToRef(result_val.toIntern());
  24292             } else {
  24293                 break :rs addend_src;
  24294             }
  24295         } else {
  24296             if (maybe_addend) |addend_val| {
  24297                 if (addend_val.isUndef(zcu)) return pt.undefRef(ty);
  24298             }
  24299             break :rs mulend2_src;
  24300         }
  24301     } else rs: {
  24302         if (maybe_mulend2) |mulend2_val| {
  24303             if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty);
  24304         }
  24305         if (maybe_addend) |addend_val| {
  24306             if (addend_val.isUndef(zcu)) return pt.undefRef(ty);
  24307         }
  24308         break :rs mulend1_src;
  24309     };
  24310 
  24311     try sema.requireRuntimeBlock(block, src, runtime_src);
  24312     return block.addInst(.{
  24313         .tag = .mul_add,
  24314         .data = .{ .pl_op = .{
  24315             .operand = addend,
  24316             .payload = try sema.addExtra(Air.Bin{
  24317                 .lhs = mulend1,
  24318                 .rhs = mulend2,
  24319             }),
  24320         } },
  24321     });
  24322 }
  24323 
  24324 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  24325     const tracy = trace(@src());
  24326     defer tracy.end();
  24327 
  24328     const pt = sema.pt;
  24329     const zcu = pt.zcu;
  24330     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24331     const modifier_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  24332     const func_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  24333     const args_src = block.builtinCallArgSrc(inst_data.src_node, 2);
  24334     const call_src = block.nodeOffset(inst_data.src_node);
  24335 
  24336     const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
  24337     const func = try sema.resolveInst(extra.callee);
  24338 
  24339     const modifier_ty = try sema.getBuiltinType(call_src, .CallModifier);
  24340     const air_ref = try sema.resolveInst(extra.modifier);
  24341     const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src);
  24342     const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{ .simple = .call_modifier });
  24343     var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier);
  24344     switch (modifier) {
  24345         // These can be upgraded to comptime or nosuspend calls.
  24346         .auto, .never_tail, .no_suspend => {
  24347             if (block.isComptime()) {
  24348                 if (modifier == .never_tail) {
  24349                     return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});
  24350                 }
  24351                 modifier = .compile_time;
  24352             } else if (extra.flags.is_nosuspend) {
  24353                 modifier = .no_suspend;
  24354             }
  24355         },
  24356         // These can be upgraded to comptime. nosuspend bit can be safely ignored.
  24357         .always_inline, .compile_time => {
  24358             _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
  24359                 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)});
  24360             };
  24361 
  24362             if (block.isComptime()) {
  24363                 modifier = .compile_time;
  24364             }
  24365         },
  24366         .always_tail => {
  24367             if (block.isComptime()) {
  24368                 modifier = .compile_time;
  24369             }
  24370         },
  24371         .never_inline => {
  24372             if (block.isComptime()) {
  24373                 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});
  24374             }
  24375         },
  24376     }
  24377 
  24378     const args = try sema.resolveInst(extra.args);
  24379 
  24380     const args_ty = sema.typeOf(args);
  24381     if (!args_ty.isTuple(zcu)) {
  24382         return sema.fail(block, args_src, "expected a tuple, found '{f}'", .{args_ty.fmt(pt)});
  24383     }
  24384 
  24385     const resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(zcu));
  24386     for (resolved_args, 0..) |*resolved, i| {
  24387         resolved.* = try sema.tupleFieldValByIndex(block, args, @intCast(i), args_ty);
  24388     }
  24389 
  24390     const callee_ty = sema.typeOf(func);
  24391     const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false);
  24392     const ensure_result_used = extra.flags.ensure_result_used;
  24393     return sema.analyzeCall(
  24394         block,
  24395         func,
  24396         func_ty,
  24397         func_src,
  24398         call_src,
  24399         modifier,
  24400         ensure_result_used,
  24401         .{ .call_builtin = .{
  24402             .call_node_offset = inst_data.src_node,
  24403             .args = resolved_args,
  24404         } },
  24405         null,
  24406         .@"@call",
  24407     );
  24408 }
  24409 
  24410 fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  24411     const pt = sema.pt;
  24412     const zcu = pt.zcu;
  24413     const ip = &zcu.intern_pool;
  24414 
  24415     const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, extended.operand).data;
  24416     const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
  24417     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small)));
  24418     assert(!flags.ptr_cast);
  24419     const inst_src = block.nodeOffset(extra.src_node);
  24420     const field_name_src = block.builtinCallArgSrc(extra.src_node, 0);
  24421     const field_ptr_src = block.builtinCallArgSrc(extra.src_node, 1);
  24422 
  24423     const parent_ptr_ty = try sema.resolveDestType(block, inst_src, extra.parent_ptr_type, .remove_eu, "@fieldParentPtr");
  24424     try sema.checkPtrType(block, inst_src, parent_ptr_ty, true);
  24425     const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
  24426     if (parent_ptr_info.flags.size != .one) {
  24427         return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)});
  24428     }
  24429     const parent_ty: Type = .fromInterned(parent_ptr_info.child);
  24430     switch (parent_ty.zigTypeTag(zcu)) {
  24431         .@"struct", .@"union" => {},
  24432         else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}),
  24433     }
  24434     try parent_ty.resolveLayout(pt);
  24435 
  24436     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
  24437     const field_index = switch (parent_ty.zigTypeTag(zcu)) {
  24438         .@"struct" => blk: {
  24439             if (parent_ty.isTuple(zcu)) {
  24440                 if (field_name.eqlSlice("len", ip)) {
  24441                     return sema.fail(block, inst_src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
  24442                 }
  24443                 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, field_name_src);
  24444             } else {
  24445                 break :blk try sema.structFieldIndex(block, parent_ty, field_name, field_name_src);
  24446             }
  24447         },
  24448         .@"union" => try sema.unionFieldIndex(block, parent_ty, field_name, field_name_src),
  24449         else => unreachable,
  24450     };
  24451     if (parent_ty.zigTypeTag(zcu) == .@"struct" and parent_ty.structFieldIsComptime(field_index, zcu)) {
  24452         return sema.fail(block, field_name_src, "cannot get @fieldParentPtr of a comptime field", .{});
  24453     }
  24454 
  24455     const field_ptr = try sema.resolveInst(extra.field_ptr);
  24456     const field_ptr_ty = sema.typeOf(field_ptr);
  24457     try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty);
  24458     const field_ptr_info = field_ptr_ty.ptrInfo(zcu);
  24459 
  24460     var actual_parent_ptr_info: InternPool.Key.PtrType = .{
  24461         .child = parent_ty.toIntern(),
  24462         .flags = .{
  24463             .alignment = try parent_ptr_ty.ptrAlignmentSema(pt),
  24464             .is_const = field_ptr_info.flags.is_const,
  24465             .is_volatile = field_ptr_info.flags.is_volatile,
  24466             .is_allowzero = field_ptr_info.flags.is_allowzero,
  24467             .address_space = field_ptr_info.flags.address_space,
  24468         },
  24469         .packed_offset = parent_ptr_info.packed_offset,
  24470     };
  24471     const field_ty = parent_ty.fieldType(field_index, zcu);
  24472     var actual_field_ptr_info: InternPool.Key.PtrType = .{
  24473         .child = field_ty.toIntern(),
  24474         .flags = .{
  24475             .alignment = try field_ptr_ty.ptrAlignmentSema(pt),
  24476             .is_const = field_ptr_info.flags.is_const,
  24477             .is_volatile = field_ptr_info.flags.is_volatile,
  24478             .is_allowzero = field_ptr_info.flags.is_allowzero,
  24479             .address_space = field_ptr_info.flags.address_space,
  24480         },
  24481         .packed_offset = field_ptr_info.packed_offset,
  24482     };
  24483     switch (parent_ty.containerLayout(zcu)) {
  24484         .auto => {
  24485             actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(
  24486                 if (zcu.typeToStruct(parent_ty)) |struct_obj|
  24487                     try field_ty.structFieldAlignmentSema(
  24488                         struct_obj.fieldAlign(ip, field_index),
  24489                         struct_obj.layout,
  24490                         pt,
  24491                     )
  24492                 else if (zcu.typeToUnion(parent_ty)) |union_obj|
  24493                     try field_ty.unionFieldAlignmentSema(
  24494                         union_obj.fieldAlign(ip, field_index),
  24495                         union_obj.flagsUnordered(ip).layout,
  24496                         pt,
  24497                     )
  24498                 else
  24499                     actual_field_ptr_info.flags.alignment,
  24500             );
  24501 
  24502             actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
  24503             actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
  24504         },
  24505         .@"extern" => {
  24506             const field_offset = parent_ty.structFieldOffset(field_index, zcu);
  24507             actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (field_offset > 0)
  24508                 Alignment.fromLog2Units(@ctz(field_offset))
  24509             else
  24510                 actual_field_ptr_info.flags.alignment);
  24511 
  24512             actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
  24513             actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
  24514         },
  24515         .@"packed" => {
  24516             const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) +
  24517                 (if (zcu.typeToStruct(parent_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, field_index) else 0) -
  24518                 actual_field_ptr_info.packed_offset.bit_offset), 8) catch
  24519                 return sema.fail(block, inst_src, "pointer bit-offset mismatch", .{});
  24520             actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (byte_offset > 0)
  24521                 Alignment.fromLog2Units(@ctz(byte_offset))
  24522             else
  24523                 actual_field_ptr_info.flags.alignment);
  24524         },
  24525     }
  24526 
  24527     const actual_field_ptr_ty = try pt.ptrTypeSema(actual_field_ptr_info);
  24528     const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, field_ptr_src);
  24529     const actual_parent_ptr_ty = try pt.ptrTypeSema(actual_parent_ptr_info);
  24530 
  24531     const result = if (try sema.resolveDefinedValue(block, field_ptr_src, casted_field_ptr)) |field_ptr_val| result: {
  24532         switch (parent_ty.zigTypeTag(zcu)) {
  24533             .@"struct" => switch (parent_ty.containerLayout(zcu)) {
  24534                 .auto => {},
  24535                 .@"extern" => {
  24536                     const byte_offset = parent_ty.structFieldOffset(field_index, zcu);
  24537                     const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty);
  24538                     break :result Air.internedToRef(parent_ptr_val.toIntern());
  24539                 },
  24540                 .@"packed" => {
  24541                     // Logic lifted from type computation above - I'm just assuming it's correct.
  24542                     // `catch unreachable` since error case handled above.
  24543                     const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) +
  24544                         zcu.structPackedFieldBitOffset(zcu.typeToStruct(parent_ty).?, field_index) -
  24545                         actual_field_ptr_info.packed_offset.bit_offset), 8) catch unreachable;
  24546                     const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty);
  24547                     break :result Air.internedToRef(parent_ptr_val.toIntern());
  24548                 },
  24549             },
  24550             .@"union" => switch (parent_ty.containerLayout(zcu)) {
  24551                 .auto => {},
  24552                 .@"extern", .@"packed" => {
  24553                     // For an extern or packed union, just coerce the pointer.
  24554                     const parent_ptr_val = try pt.getCoerced(field_ptr_val, actual_parent_ptr_ty);
  24555                     break :result Air.internedToRef(parent_ptr_val.toIntern());
  24556                 },
  24557             },
  24558             else => unreachable,
  24559         }
  24560 
  24561         const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: {
  24562             const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) {
  24563                 .ptr => |ptr| ptr,
  24564                 else => break :opt_field null,
  24565             };
  24566             if (ptr.byte_offset != 0) break :opt_field null;
  24567             break :opt_field switch (ptr.base_addr) {
  24568                 .field => |field| field,
  24569                 else => null,
  24570             };
  24571         };
  24572 
  24573         const field = opt_field orelse {
  24574             return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
  24575         };
  24576 
  24577         if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) {
  24578             return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
  24579         }
  24580 
  24581         if (field.index != field_index) {
  24582             return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{
  24583                 field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt),
  24584             });
  24585         }
  24586         break :result try sema.coerce(block, actual_parent_ptr_ty, Air.internedToRef(field.base), inst_src);
  24587     } else result: {
  24588         try sema.requireRuntimeBlock(block, inst_src, field_ptr_src);
  24589         break :result try block.addInst(.{
  24590             .tag = .field_parent_ptr,
  24591             .data = .{ .ty_pl = .{
  24592                 .ty = Air.internedToRef(actual_parent_ptr_ty.toIntern()),
  24593                 .payload = try block.sema.addExtra(Air.FieldParentPtr{
  24594                     .field_ptr = casted_field_ptr,
  24595                     .field_index = @intCast(field_index),
  24596                 }),
  24597             } },
  24598         });
  24599     };
  24600     return sema.ptrCastFull(block, flags, inst_src, result, inst_src, parent_ptr_ty, "@fieldParentPtr");
  24601 }
  24602 
  24603 fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte_subtract: u64, new_ty: Type) !Value {
  24604     const pt = sema.pt;
  24605     const zcu = pt.zcu;
  24606     if (byte_subtract == 0) return pt.getCoerced(ptr_val, new_ty);
  24607     var ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
  24608         .undef => return sema.failWithUseOfUndef(block, src, null),
  24609         .ptr => |ptr| ptr,
  24610         else => unreachable,
  24611     };
  24612     if (ptr.byte_offset < byte_subtract) {
  24613         return sema.failWithOwnedErrorMsg(block, msg: {
  24614             const msg = try sema.errMsg(src, "pointer computation here causes illegal behavior", .{});
  24615             errdefer msg.destroy(sema.gpa);
  24616             try sema.errNote(src, msg, "resulting pointer exceeds bounds of containing value which may trigger overflow", .{});
  24617             break :msg msg;
  24618         });
  24619     }
  24620     ptr.byte_offset -= byte_subtract;
  24621     ptr.ty = new_ty.toIntern();
  24622     return Value.fromInterned(try pt.intern(.{ .ptr = ptr }));
  24623 }
  24624 
  24625 fn zirMinMax(
  24626     sema: *Sema,
  24627     block: *Block,
  24628     inst: Zir.Inst.Index,
  24629     comptime air_tag: Air.Inst.Tag,
  24630 ) CompileError!Air.Inst.Ref {
  24631     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24632     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  24633     const src = block.nodeOffset(inst_data.src_node);
  24634     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  24635     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  24636     const lhs = try sema.resolveInst(extra.lhs);
  24637     const rhs = try sema.resolveInst(extra.rhs);
  24638     return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src });
  24639 }
  24640 
  24641 fn zirMinMaxMulti(
  24642     sema: *Sema,
  24643     block: *Block,
  24644     extended: Zir.Inst.Extended.InstData,
  24645     comptime air_tag: Air.Inst.Tag,
  24646 ) CompileError!Air.Inst.Ref {
  24647     const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
  24648     const src_node = extra.data.src_node;
  24649     const src = block.nodeOffset(src_node);
  24650     const operands = sema.code.refSlice(extra.end, extended.small);
  24651 
  24652     const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  24653     const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len);
  24654 
  24655     for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| {
  24656         op_src.* = block.builtinCallArgSrc(src_node, @intCast(i));
  24657         air_ref.* = try sema.resolveInst(zir_ref);
  24658     }
  24659 
  24660     return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs);
  24661 }
  24662 
  24663 fn analyzeMinMax(
  24664     sema: *Sema,
  24665     block: *Block,
  24666     src: LazySrcLoc,
  24667     comptime air_tag: Air.Inst.Tag,
  24668     operands: []const Air.Inst.Ref,
  24669     operand_srcs: []const LazySrcLoc,
  24670 ) CompileError!Air.Inst.Ref {
  24671     assert(operands.len == operand_srcs.len);
  24672     assert(operands.len > 0);
  24673 
  24674     const pt = sema.pt;
  24675     const zcu = pt.zcu;
  24676 
  24677     // This function has the signature `fn (Value, Value, *Zcu) Value`.
  24678     // It is only used on scalar values, although the values may have different types.
  24679     // If either operand is undef, it returns undef.
  24680     const opFunc = switch (air_tag) {
  24681         .min => Value.numberMin,
  24682         .max => Value.numberMax,
  24683         else => comptime unreachable,
  24684     };
  24685 
  24686     if (operands.len == 1) {
  24687         try sema.checkNumericType(block, operand_srcs[0], sema.typeOf(operands[0]));
  24688         return operands[0];
  24689     }
  24690 
  24691     // First, basic type validation; we'll make sure all the operands are numeric and agree on vector length.
  24692     // This value will be `null` for a scalar type, otherwise the length of the vector type.
  24693     const vector_len: ?u64 = vec_len: {
  24694         const first_operand_ty = sema.typeOf(operands[0]);
  24695         try sema.checkNumericType(block, operand_srcs[0], first_operand_ty);
  24696         if (first_operand_ty.zigTypeTag(zcu) == .vector) {
  24697             const vec_len = first_operand_ty.vectorLen(zcu);
  24698             for (operands[1..], operand_srcs[1..]) |operand, operand_src| {
  24699                 const operand_ty = sema.typeOf(operand);
  24700                 try sema.checkNumericType(block, operand_src, operand_ty);
  24701                 if (operand_ty.zigTypeTag(zcu) != .vector) {
  24702                     return sema.failWithOwnedErrorMsg(block, msg: {
  24703                         const msg = try sema.errMsg(operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)});
  24704                         errdefer msg.destroy(zcu.gpa);
  24705                         try sema.errNote(operand_srcs[0], msg, "vector operand here", .{});
  24706                         break :msg msg;
  24707                     });
  24708                 }
  24709                 if (operand_ty.vectorLen(zcu) != vec_len) {
  24710                     return sema.failWithOwnedErrorMsg(block, msg: {
  24711                         const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{f}'", .{ vec_len, operand_ty.fmt(pt) });
  24712                         errdefer msg.destroy(zcu.gpa);
  24713                         try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len});
  24714                         break :msg msg;
  24715                     });
  24716                 }
  24717             }
  24718             break :vec_len vec_len;
  24719         } else {
  24720             for (operands[1..], operand_srcs[1..]) |operand, operand_src| {
  24721                 const operand_ty = sema.typeOf(operand);
  24722                 try sema.checkNumericType(block, operand_src, operand_ty);
  24723                 if (operand_ty.zigTypeTag(zcu) == .vector) {
  24724                     return sema.failWithOwnedErrorMsg(block, msg: {
  24725                         const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{f}'", .{first_operand_ty.fmt(pt)});
  24726                         errdefer msg.destroy(zcu.gpa);
  24727                         try sema.errNote(operand_src, msg, "vector operand here", .{});
  24728                         break :msg msg;
  24729                     });
  24730                 }
  24731             }
  24732             break :vec_len null;
  24733         }
  24734     };
  24735 
  24736     // Now we want to look at the scalar types. If any is a float, our result will be a float. This
  24737     // union is in "priority" order: `float` overrides `comptime_float` overrides `int`.
  24738     const TypeStrat = union(enum) {
  24739         float: Type,
  24740         comptime_float,
  24741         int: struct {
  24742             /// If this is still `true` at the end, we will just use a `comptime_int`.
  24743             all_comptime_int: bool,
  24744             // These two fields tells us about the *result* type, which is refined based on operand types.
  24745             // e.g. `@max(u32, i64)` results in a `u63`, because the result is >=0 and <=maxInt(i64).
  24746             result_min: Value,
  24747             result_max: Value,
  24748             // These two fields tell us the *intermediate* type to use for actually computing the min/max.
  24749             // e.g. `@max(u32, i64)` uses an intermediate `i64`, because it can fit all our operands.
  24750             operand_min: Value,
  24751             operand_max: Value,
  24752         },
  24753         none,
  24754     };
  24755     var cur_strat: TypeStrat = .none;
  24756     for (operands) |operand| {
  24757         const operand_scalar_ty = sema.typeOf(operand).scalarType(zcu);
  24758         const want_strat: TypeStrat = switch (operand_scalar_ty.zigTypeTag(zcu)) {
  24759             .comptime_int => s: {
  24760                 const val = (try sema.resolveValueResolveLazy(operand)).?;
  24761                 if (val.isUndef(zcu)) break :s .none;
  24762                 break :s .{ .int = .{
  24763                     .all_comptime_int = true,
  24764                     .result_min = val,
  24765                     .result_max = val,
  24766                     .operand_min = val,
  24767                     .operand_max = val,
  24768                 } };
  24769             },
  24770             .comptime_float => .comptime_float,
  24771             .float => .{ .float = operand_scalar_ty },
  24772             .int => s: {
  24773                 // If the *value* is comptime-known, we will use that to get tighter bounds. If #3806
  24774                 // is accepted and implemented, so that integer literals have a tightly-bounded ranged
  24775                 // integer type (and `comptime_int` ceases to exist), this block should probably go away
  24776                 // (replaced with just the simple calls to `Type.minInt`/`Type.maxInt`) so that we only
  24777                 // use the input *types* to determine the result type.
  24778                 const min: Value, const max: Value = bounds: {
  24779                     if (try sema.resolveValueResolveLazy(operand)) |operand_val| {
  24780                         if (vector_len) |len| {
  24781                             var min = try operand_val.elemValue(pt, 0);
  24782                             var max = min;
  24783                             for (1..@intCast(len)) |elem_idx| {
  24784                                 const elem_val = try operand_val.elemValue(pt, elem_idx);
  24785                                 min = Value.numberMin(min, elem_val, zcu);
  24786                                 max = Value.numberMax(max, elem_val, zcu);
  24787                             }
  24788                             if (!min.isUndef(zcu) and !max.isUndef(zcu)) {
  24789                                 break :bounds .{ min, max };
  24790                             }
  24791                         } else {
  24792                             if (!operand_val.isUndef(zcu)) {
  24793                                 break :bounds .{ operand_val, operand_val };
  24794                             }
  24795                         }
  24796                     }
  24797                     break :bounds .{
  24798                         try operand_scalar_ty.minInt(pt, operand_scalar_ty),
  24799                         try operand_scalar_ty.maxInt(pt, operand_scalar_ty),
  24800                     };
  24801                 };
  24802                 break :s .{ .int = .{
  24803                     .all_comptime_int = false,
  24804                     .result_min = min,
  24805                     .result_max = max,
  24806                     .operand_min = min,
  24807                     .operand_max = max,
  24808                 } };
  24809             },
  24810             else => unreachable,
  24811         };
  24812         if (@intFromEnum(want_strat) < @intFromEnum(cur_strat)) {
  24813             // `want_strat` overrides `cur_strat`.
  24814             cur_strat = want_strat;
  24815         } else if (@intFromEnum(want_strat) == @intFromEnum(cur_strat)) {
  24816             // The behavior depends on the tag.
  24817             switch (cur_strat) {
  24818                 .none, .comptime_float => {}, // no payload, so nop
  24819                 .float => |cur_float| {
  24820                     const want_float = want_strat.float;
  24821                     // Select the larger bit size. If the bit size is the same, select whichever is not c_longdouble.
  24822                     const cur_bits = cur_float.floatBits(zcu.getTarget());
  24823                     const want_bits = want_float.floatBits(zcu.getTarget());
  24824                     if (want_bits > cur_bits or
  24825                         (want_bits == cur_bits and
  24826                             cur_float.toIntern() == .c_longdouble_type and
  24827                             want_float.toIntern() != .c_longdouble_type))
  24828                     {
  24829                         cur_strat = want_strat;
  24830                     }
  24831                 },
  24832                 .int => |*cur_int| {
  24833                     const want_int = want_strat.int;
  24834                     if (!want_int.all_comptime_int) cur_int.all_comptime_int = false;
  24835                     cur_int.result_min = opFunc(cur_int.result_min, want_int.result_min, zcu);
  24836                     cur_int.result_max = opFunc(cur_int.result_max, want_int.result_max, zcu);
  24837                     cur_int.operand_min = Value.numberMin(cur_int.operand_min, want_int.operand_min, zcu);
  24838                     cur_int.operand_max = Value.numberMax(cur_int.operand_max, want_int.operand_max, zcu);
  24839                 },
  24840             }
  24841         }
  24842     }
  24843 
  24844     // Use `cur_strat` to actually resolve the result type (and intermediate type).
  24845     const result_scalar_ty: Type, const intermediate_scalar_ty: Type = switch (cur_strat) {
  24846         .float => |ty| .{ ty, ty },
  24847         .comptime_float => .{ .comptime_float, .comptime_float },
  24848         .int => |int| if (int.all_comptime_int) .{
  24849             .comptime_int,
  24850             .comptime_int,
  24851         } else .{
  24852             try pt.intFittingRange(int.result_min, int.result_max),
  24853             try pt.intFittingRange(int.operand_min, int.operand_max),
  24854         },
  24855         .none => .{ .comptime_int, .comptime_int }, // all undef comptime ints
  24856     };
  24857     const result_ty: Type = if (vector_len) |l| try pt.vectorType(.{
  24858         .len = @intCast(l),
  24859         .child = result_scalar_ty.toIntern(),
  24860     }) else result_scalar_ty;
  24861     const intermediate_ty: Type = if (vector_len) |l| try pt.vectorType(.{
  24862         .len = @intCast(l),
  24863         .child = intermediate_scalar_ty.toIntern(),
  24864     }) else intermediate_scalar_ty;
  24865 
  24866     // This value, if not `null`, will have type `intermediate_ty`.
  24867     const comptime_part: ?Value = ct: {
  24868         // Contains the comptime-known scalar result values.
  24869         // Values are scalars with no particular type.
  24870         // `elems.len` is `vector_len orelse 1`.
  24871         const elems: []InternPool.Index = try sema.arena.alloc(
  24872             InternPool.Index,
  24873             try sema.usizeCast(block, src, vector_len orelse 1),
  24874         );
  24875         // If `false`, we've not seen any comptime-known operand yet, so `elems` contains `undefined`.
  24876         // Otherwise, `elems` is populated with the comptime-known results so far.
  24877         var elems_populated = false;
  24878         // Populated when we see a runtime-known operand.
  24879         var opt_runtime_src: ?LazySrcLoc = null;
  24880 
  24881         for (operands, operand_srcs) |operand, operand_src| {
  24882             const operand_val = try sema.resolveValueResolveLazy(operand) orelse {
  24883                 if (opt_runtime_src == null) opt_runtime_src = operand_src;
  24884                 continue;
  24885             };
  24886             if (vector_len) |len| {
  24887                 // Vector case; apply `opFunc` to each element.
  24888                 if (elems_populated) {
  24889                     for (elems, 0..@intCast(len)) |*elem, elem_idx| {
  24890                         const new_elem = try operand_val.elemValue(pt, elem_idx);
  24891                         elem.* = opFunc(.fromInterned(elem.*), new_elem, zcu).toIntern();
  24892                     }
  24893                 } else {
  24894                     elems_populated = true;
  24895                     for (elems, 0..@intCast(len)) |*elem_out, elem_idx| {
  24896                         elem_out.* = (try operand_val.elemValue(pt, elem_idx)).toIntern();
  24897                     }
  24898                 }
  24899             } else {
  24900                 // Scalar case; just apply `opFunc`.
  24901                 if (elems_populated) {
  24902                     elems[0] = opFunc(.fromInterned(elems[0]), operand_val, zcu).toIntern();
  24903                 } else {
  24904                     elems_populated = true;
  24905                     elems[0] = operand_val.toIntern();
  24906                 }
  24907             }
  24908         }
  24909         const runtime_src = opt_runtime_src orelse {
  24910             // The result is comptime-known. Coerce each element to its scalar type.
  24911             assert(elems_populated);
  24912             for (elems) |*elem| {
  24913                 if (Value.fromInterned(elem.*).isUndef(zcu)) {
  24914                     elem.* = (try pt.undefValue(result_scalar_ty)).toIntern();
  24915                 } else {
  24916                     // This coercion will always succeed, because `result_scalar_ty` can definitely hold the result.
  24917                     const coerced_ref = try sema.coerce(block, result_scalar_ty, Air.internedToRef(elem.*), .unneeded);
  24918                     elem.* = coerced_ref.toInterned().?;
  24919                 }
  24920             }
  24921             if (vector_len == null) return Air.internedToRef(elems[0]);
  24922             return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern());
  24923         };
  24924         _ = runtime_src;
  24925         // The result is runtime-known.
  24926         // Coerce each element to the intermediate scalar type, unless there were no comptime-known operands.
  24927         if (!elems_populated) break :ct null;
  24928         for (elems) |*elem| {
  24929             if (Value.fromInterned(elem.*).isUndef(zcu)) {
  24930                 elem.* = (try pt.undefValue(intermediate_scalar_ty)).toIntern();
  24931             } else {
  24932                 // This coercion will always succeed, because `intermediate_scalar_ty` can definitely hold all operands.
  24933                 const coerced_ref = try sema.coerce(block, intermediate_scalar_ty, Air.internedToRef(elem.*), .unneeded);
  24934                 elem.* = coerced_ref.toInterned().?;
  24935             }
  24936         }
  24937         break :ct if (vector_len != null)
  24938             try pt.aggregateValue(intermediate_ty, elems)
  24939         else
  24940             .fromInterned(elems[0]);
  24941     };
  24942 
  24943     // Time to emit the runtime operations. All runtime-known peers are coerced to `intermediate_ty`, and we cast down to `result_ty` at the end.
  24944 
  24945     // `.none` indicates no result so far.
  24946     var cur_result: Air.Inst.Ref = if (comptime_part) |val| Air.internedToRef(val.toIntern()) else .none;
  24947     for (operands, operand_srcs) |operand, operand_src| {
  24948         if (try sema.isComptimeKnown(operand)) continue; // already in `comptime_part`
  24949         // This coercion could fail; e.g. coercing a runtime integer peer to a `comptime_float` in a case like `@min(runtime_int, 1.5)`.
  24950         const operand_coerced = try sema.coerce(block, intermediate_ty, operand, operand_src);
  24951         if (cur_result == .none) {
  24952             cur_result = operand_coerced;
  24953         } else {
  24954             cur_result = try block.addBinOp(air_tag, cur_result, operand_coerced);
  24955         }
  24956     }
  24957 
  24958     assert(cur_result != .none);
  24959     assert(sema.typeOf(cur_result).toIntern() == intermediate_ty.toIntern());
  24960 
  24961     // If there is a comptime-known undef operand, we actually return comptime-known undef -- but we had to do the runtime stuff to check for coercion errors.
  24962     if (comptime_part) |val| {
  24963         if (val.isUndef(zcu)) {
  24964             return pt.undefRef(result_ty);
  24965         }
  24966     }
  24967 
  24968     if (result_ty.toIntern() == intermediate_ty.toIntern()) {
  24969         // No final cast needed; we're all done.
  24970         return cur_result;
  24971     }
  24972 
  24973     // A final cast is needed. The only case where `intermediate_ty` is different is for integers,
  24974     // where we have refined the range, so we should be doing an intcast.
  24975     assert(intermediate_scalar_ty.zigTypeTag(zcu) == .int);
  24976     assert(result_scalar_ty.zigTypeTag(zcu) == .int);
  24977     return block.addTyOp(.intcast, result_ty, cur_result);
  24978 }
  24979 
  24980 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref {
  24981     const pt = sema.pt;
  24982     const zcu = pt.zcu;
  24983     const ptr_ty = sema.typeOf(ptr);
  24984     const info = ptr_ty.ptrInfo(zcu);
  24985     if (info.flags.size == .one) {
  24986         // Already an array pointer.
  24987         return ptr;
  24988     }
  24989     const new_ty = try pt.ptrTypeSema(.{
  24990         .child = (try pt.arrayType(.{
  24991             .len = len,
  24992             .sentinel = info.sentinel,
  24993             .child = info.child,
  24994         })).toIntern(),
  24995         .flags = .{
  24996             .alignment = info.flags.alignment,
  24997             .is_const = info.flags.is_const,
  24998             .is_volatile = info.flags.is_volatile,
  24999             .is_allowzero = info.flags.is_allowzero,
  25000             .address_space = info.flags.address_space,
  25001         },
  25002     });
  25003     const non_slice_ptr = if (info.flags.size == .slice)
  25004         try block.addTyOp(.slice_ptr, ptr_ty.slicePtrFieldType(zcu), ptr)
  25005     else
  25006         ptr;
  25007     return block.addBitCast(new_ty, non_slice_ptr);
  25008 }
  25009 
  25010 fn zirMemcpy(
  25011     sema: *Sema,
  25012     block: *Block,
  25013     inst: Zir.Inst.Index,
  25014     air_tag: Air.Inst.Tag,
  25015     check_aliasing: bool,
  25016 ) CompileError!void {
  25017     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  25018     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  25019     const src = block.nodeOffset(inst_data.src_node);
  25020     const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  25021     const src_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  25022     const dest_ptr = try sema.resolveInst(extra.lhs);
  25023     const src_ptr = try sema.resolveInst(extra.rhs);
  25024     const dest_ty = sema.typeOf(dest_ptr);
  25025     const src_ty = sema.typeOf(src_ptr);
  25026     const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr);
  25027     const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
  25028     const pt = sema.pt;
  25029     const zcu = pt.zcu;
  25030 
  25031     if (dest_ty.isConstPtr(zcu)) {
  25032         return sema.fail(block, dest_src, "cannot copy to constant pointer", .{});
  25033     }
  25034 
  25035     if (dest_len == .none and src_len == .none) {
  25036         const msg = msg: {
  25037             const msg = try sema.errMsg(src, "unknown copy length", .{});
  25038             errdefer msg.destroy(sema.gpa);
  25039             try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{
  25040                 dest_ty.fmt(pt),
  25041             });
  25042             try sema.errNote(src_src, msg, "source type '{f}' provides no length", .{
  25043                 src_ty.fmt(pt),
  25044             });
  25045             break :msg msg;
  25046         };
  25047         return sema.failWithOwnedErrorMsg(block, msg);
  25048     }
  25049 
  25050     const dest_elem_ty = dest_ty.indexablePtrElem(zcu);
  25051     const src_elem_ty = src_ty.indexablePtrElem(zcu);
  25052 
  25053     const imc = try sema.coerceInMemoryAllowed(
  25054         block,
  25055         dest_elem_ty,
  25056         src_elem_ty,
  25057         false,
  25058         zcu.getTarget(),
  25059         dest_src,
  25060         src_src,
  25061         null,
  25062     );
  25063     if (imc != .ok) return sema.failWithOwnedErrorMsg(block, msg: {
  25064         const msg = try sema.errMsg(
  25065             src,
  25066             "pointer element type '{f}' cannot coerce into element type '{f}'",
  25067             .{ src_elem_ty.fmt(pt), dest_elem_ty.fmt(pt) },
  25068         );
  25069         errdefer msg.destroy(sema.gpa);
  25070         try imc.report(sema, src, msg);
  25071         break :msg msg;
  25072     });
  25073 
  25074     var len_val: ?Value = null;
  25075 
  25076     if (dest_len != .none and src_len != .none) check: {
  25077         // If we can check at compile-time, no need for runtime safety.
  25078         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  25079             len_val = dest_len_val;
  25080             if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  25081                 if (!(try sema.valuesEqual(dest_len_val, src_len_val, .usize))) {
  25082                     const msg = msg: {
  25083                         const msg = try sema.errMsg(src, "non-matching copy lengths", .{});
  25084                         errdefer msg.destroy(sema.gpa);
  25085                         try sema.errNote(dest_src, msg, "length {f} here", .{
  25086                             dest_len_val.fmtValueSema(pt, sema),
  25087                         });
  25088                         try sema.errNote(src_src, msg, "length {f} here", .{
  25089                             src_len_val.fmtValueSema(pt, sema),
  25090                         });
  25091                         break :msg msg;
  25092                     };
  25093                     return sema.failWithOwnedErrorMsg(block, msg);
  25094                 }
  25095                 break :check;
  25096             }
  25097         } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  25098             len_val = src_len_val;
  25099         }
  25100 
  25101         if (block.wantSafety()) {
  25102             const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
  25103             try sema.addSafetyCheck(block, src, ok, .copy_len_mismatch);
  25104         }
  25105     } else if (dest_len != .none) {
  25106         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  25107             len_val = dest_len_val;
  25108         }
  25109     } else if (src_len != .none) {
  25110         if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  25111             len_val = src_len_val;
  25112         }
  25113     }
  25114 
  25115     zero_bit: {
  25116         const src_comptime = try src_elem_ty.comptimeOnlySema(pt);
  25117         const dest_comptime = try dest_elem_ty.comptimeOnlySema(pt);
  25118         assert(src_comptime == dest_comptime); // IMC
  25119         if (src_comptime) break :zero_bit;
  25120 
  25121         const src_has_bits = try src_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt);
  25122         const dest_has_bits = try dest_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt);
  25123         assert(src_has_bits == dest_has_bits); // IMC
  25124         if (src_has_bits) break :zero_bit;
  25125 
  25126         // The element type is zero-bit. We've done all validation (aside from the aliasing check,
  25127         // which we must skip) so we're done.
  25128         return;
  25129     }
  25130 
  25131     const runtime_src = rs: {
  25132         const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src;
  25133         const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src;
  25134 
  25135         const raw_dest_ptr = if (dest_ty.isSlice(zcu)) dest_ptr_val.slicePtr(zcu) else dest_ptr_val;
  25136         const raw_src_ptr = if (src_ty.isSlice(zcu)) src_ptr_val.slicePtr(zcu) else src_ptr_val;
  25137 
  25138         const len_u64 = try len_val.?.toUnsignedIntSema(pt);
  25139 
  25140         if (check_aliasing) {
  25141             if (Value.doPointersOverlap(
  25142                 raw_src_ptr,
  25143                 raw_dest_ptr,
  25144                 len_u64,
  25145                 zcu,
  25146             )) return sema.fail(block, src, "'@memcpy' arguments alias", .{});
  25147         }
  25148 
  25149         if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src;
  25150 
  25151         // Because comptime pointer access is a somewhat expensive operation, we implement @memcpy
  25152         // as one load and store of an array, rather than N loads and stores of individual elements.
  25153 
  25154         const array_ty = try pt.arrayType(.{
  25155             .child = dest_elem_ty.toIntern(),
  25156             .len = len_u64,
  25157         });
  25158 
  25159         const dest_array_ptr_ty = try pt.ptrType(info: {
  25160             var info = dest_ty.ptrInfo(zcu);
  25161             info.flags.size = .one;
  25162             info.child = array_ty.toIntern();
  25163             info.sentinel = .none;
  25164             break :info info;
  25165         });
  25166         const src_array_ptr_ty = try pt.ptrType(info: {
  25167             var info = src_ty.ptrInfo(zcu);
  25168             info.flags.size = .one;
  25169             info.child = array_ty.toIntern();
  25170             info.sentinel = .none;
  25171             break :info info;
  25172         });
  25173 
  25174         const coerced_dest_ptr = try pt.getCoerced(raw_dest_ptr, dest_array_ptr_ty);
  25175         const coerced_src_ptr = try pt.getCoerced(raw_src_ptr, src_array_ptr_ty);
  25176 
  25177         const array_val = try sema.pointerDeref(block, src_src, coerced_src_ptr, src_array_ptr_ty) orelse break :rs src_src;
  25178         try sema.storePtrVal(block, dest_src, coerced_dest_ptr, array_val, array_ty);
  25179         return;
  25180     };
  25181 
  25182     // If the length is comptime-known, then upgrade src and destination types
  25183     // into pointer-to-array. At this point we know they are both pointers
  25184     // already.
  25185     var new_dest_ptr = dest_ptr;
  25186     var new_src_ptr = src_ptr;
  25187     if (len_val) |val| {
  25188         const len = try val.toUnsignedIntSema(pt);
  25189         if (len == 0) {
  25190             // This AIR instruction guarantees length > 0 if it is comptime-known.
  25191             return;
  25192         }
  25193         new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len);
  25194         new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
  25195     }
  25196 
  25197     if (dest_len != .none) {
  25198         // Change the src from slice to a many pointer, to avoid multiple ptr
  25199         // slice extractions in AIR instructions.
  25200         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  25201         if (new_src_ptr_ty.isSlice(zcu)) {
  25202             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  25203         }
  25204     } else if (dest_len == .none and len_val == null) {
  25205         // Change the dest to a slice, since its type must have the length.
  25206         const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr);
  25207         new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, LazySrcLoc.unneeded, dest_src, dest_src, dest_src, false);
  25208         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  25209         if (new_src_ptr_ty.isSlice(zcu)) {
  25210             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  25211         }
  25212     }
  25213 
  25214     try sema.requireRuntimeBlock(block, src, runtime_src);
  25215     try sema.validateRuntimeValue(block, dest_src, dest_ptr);
  25216     try sema.validateRuntimeValue(block, src_src, src_ptr);
  25217 
  25218     // Aliasing safety check.
  25219     if (check_aliasing and block.wantSafety()) {
  25220         const len = if (len_val) |v|
  25221             Air.internedToRef(v.toIntern())
  25222         else if (dest_len != .none)
  25223             dest_len
  25224         else
  25225             src_len;
  25226 
  25227         // Extract raw pointer from dest slice. The AIR instructions could support them, but
  25228         // it would cause redundant machine code instructions.
  25229         const new_dest_ptr_ty = sema.typeOf(new_dest_ptr);
  25230         const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(zcu))
  25231             try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty)
  25232         else if (new_dest_ptr_ty.ptrSize(zcu) == .one) ptr: {
  25233             var dest_manyptr_ty_key = zcu.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type;
  25234             assert(dest_manyptr_ty_key.flags.size == .one);
  25235             dest_manyptr_ty_key.child = dest_elem_ty.toIntern();
  25236             dest_manyptr_ty_key.flags.size = .many;
  25237             break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(dest_manyptr_ty_key), new_dest_ptr, dest_src);
  25238         } else new_dest_ptr;
  25239 
  25240         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  25241         const raw_src_ptr = if (new_src_ptr_ty.isSlice(zcu))
  25242             try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty)
  25243         else if (new_src_ptr_ty.ptrSize(zcu) == .one) ptr: {
  25244             var src_manyptr_ty_key = zcu.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type;
  25245             assert(src_manyptr_ty_key.flags.size == .one);
  25246             src_manyptr_ty_key.child = src_elem_ty.toIntern();
  25247             src_manyptr_ty_key.flags.size = .many;
  25248             break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(src_manyptr_ty_key), new_src_ptr, src_src);
  25249         } else new_src_ptr;
  25250 
  25251         // ok1: dest >= src + len
  25252         // ok2: src >= dest + len
  25253         const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src);
  25254         const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
  25255         const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
  25256         const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
  25257         const ok = try block.addBinOp(.bool_or, ok1, ok2);
  25258         try sema.addSafetyCheck(block, src, ok, .memcpy_alias);
  25259     }
  25260 
  25261     _ = try block.addInst(.{
  25262         .tag = air_tag,
  25263         .data = .{ .bin_op = .{
  25264             .lhs = new_dest_ptr,
  25265             .rhs = new_src_ptr,
  25266         } },
  25267     });
  25268 }
  25269 
  25270 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  25271     const pt = sema.pt;
  25272     const zcu = pt.zcu;
  25273     const gpa = sema.gpa;
  25274     const ip = &zcu.intern_pool;
  25275     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  25276     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  25277     const src = block.nodeOffset(inst_data.src_node);
  25278     const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  25279     const value_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  25280     const dest_ptr = try sema.resolveInst(extra.lhs);
  25281     const uncoerced_elem = try sema.resolveInst(extra.rhs);
  25282     const dest_ptr_ty = sema.typeOf(dest_ptr);
  25283     try checkMemOperand(sema, block, dest_src, dest_ptr_ty);
  25284 
  25285     if (dest_ptr_ty.isConstPtr(zcu)) {
  25286         return sema.fail(block, dest_src, "cannot memset constant pointer", .{});
  25287     }
  25288 
  25289     const dest_elem_ty: Type = dest_elem_ty: {
  25290         const ptr_info = dest_ptr_ty.ptrInfo(zcu);
  25291         switch (ptr_info.flags.size) {
  25292             .slice => break :dest_elem_ty .fromInterned(ptr_info.child),
  25293             .one => {
  25294                 if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) {
  25295                     break :dest_elem_ty Type.fromInterned(ptr_info.child).childType(zcu);
  25296                 }
  25297             },
  25298             .many, .c => {},
  25299         }
  25300         return sema.failWithOwnedErrorMsg(block, msg: {
  25301             const msg = try sema.errMsg(src, "unknown @memset length", .{});
  25302             errdefer msg.destroy(sema.gpa);
  25303             try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{
  25304                 dest_ptr_ty.fmt(pt),
  25305             });
  25306             break :msg msg;
  25307         });
  25308     };
  25309 
  25310     const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src);
  25311 
  25312     const runtime_src = rs: {
  25313         const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src;
  25314         const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), dest_src);
  25315         const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src;
  25316         const len_u64 = try len_val.toUnsignedIntSema(pt);
  25317         const len = try sema.usizeCast(block, dest_src, len_u64);
  25318         if (len == 0) {
  25319             // This AIR instruction guarantees length > 0 if it is comptime-known.
  25320             return;
  25321         }
  25322 
  25323         if (!sema.isComptimeMutablePtr(ptr_val)) break :rs dest_src;
  25324         const elem_val = try sema.resolveValue(elem) orelse break :rs value_src;
  25325         const array_ty = try pt.arrayType(.{
  25326             .child = dest_elem_ty.toIntern(),
  25327             .len = len_u64,
  25328         });
  25329         const array_val = try pt.aggregateSplatValue(array_ty, elem_val);
  25330         const array_ptr_ty = ty: {
  25331             var info = dest_ptr_ty.ptrInfo(zcu);
  25332             info.flags.size = .one;
  25333             info.child = array_ty.toIntern();
  25334             break :ty try pt.ptrType(info);
  25335         };
  25336         const raw_ptr_val = if (dest_ptr_ty.isSlice(zcu)) ptr_val.slicePtr(zcu) else ptr_val;
  25337         const array_ptr_val = try pt.getCoerced(raw_ptr_val, array_ptr_ty);
  25338         return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty);
  25339     };
  25340 
  25341     try sema.requireRuntimeBlock(block, src, runtime_src);
  25342     try sema.validateRuntimeValue(block, dest_src, dest_ptr);
  25343     try sema.validateRuntimeValue(block, value_src, elem);
  25344 
  25345     _ = try block.addInst(.{
  25346         .tag = if (block.wantSafety()) .memset_safe else .memset,
  25347         .data = .{ .bin_op = .{
  25348             .lhs = dest_ptr,
  25349             .rhs = elem,
  25350         } },
  25351     });
  25352 }
  25353 
  25354 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  25355     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  25356     const src = block.nodeOffset(inst_data.src_node);
  25357     return sema.failWithUseOfAsync(block, src);
  25358 }
  25359 
  25360 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  25361     const tracy = trace(@src());
  25362     defer tracy.end();
  25363 
  25364     const pt = sema.pt;
  25365     const zcu = pt.zcu;
  25366     const ip = &zcu.intern_pool;
  25367     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  25368     const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
  25369     const target = zcu.getTarget();
  25370 
  25371     const cc_src = block.src(.{ .node_offset_fn_type_cc = inst_data.src_node });
  25372     const ret_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node });
  25373     const has_body = extra.data.body_len != 0;
  25374 
  25375     var extra_index: usize = extra.end;
  25376 
  25377     const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
  25378         const body_len = sema.code.extra[extra_index];
  25379         extra_index += 1;
  25380         const body = sema.code.bodySlice(extra_index, body_len);
  25381         extra_index += body.len;
  25382 
  25383         const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention);
  25384         const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, .{ .simple = .@"callconv" });
  25385         break :blk try sema.analyzeValueAsCallconv(block, cc_src, val);
  25386     } else if (extra.data.bits.has_cc_ref) blk: {
  25387         const cc_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  25388         extra_index += 1;
  25389         const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention);
  25390         const uncoerced_cc = try sema.resolveInst(cc_ref);
  25391         const coerced_cc = try sema.coerce(block, cc_ty, uncoerced_cc, cc_src);
  25392         const cc_val = try sema.resolveConstDefinedValue(block, cc_src, coerced_cc, .{ .simple = .@"callconv" });
  25393         break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val);
  25394     } else cc: {
  25395         if (has_body) {
  25396             const func_decl_nav = sema.owner.unwrap().nav_val;
  25397             const func_decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(&zcu.intern_pool) orelse return error.AnalysisFail;
  25398             const zir_decl = sema.code.getDeclaration(func_decl_inst);
  25399             if (zir_decl.linkage == .@"export") {
  25400                 break :cc target.cCallingConvention() orelse {
  25401                     // This target has no default C calling convention. We sometimes trigger a similar
  25402                     // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency,
  25403                     // let's eval that now and just get the transitive error. (It's guaranteed to error
  25404                     // because it does the exact `cCallingConvention` call we just did.)
  25405                     const cc_type = try sema.getBuiltinType(cc_src, .CallingConvention);
  25406                     _ = try sema.namespaceLookupVal(
  25407                         block,
  25408                         LazySrcLoc.unneeded,
  25409                         cc_type.getNamespaceIndex(zcu),
  25410                         try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls),
  25411                     );
  25412                     // The above should have errored.
  25413                     @panic("std.builtin is corrupt");
  25414                 };
  25415             }
  25416         }
  25417         break :cc .auto;
  25418     };
  25419 
  25420     const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
  25421         const body_len = sema.code.extra[extra_index];
  25422         extra_index += 1;
  25423         const body = sema.code.bodySlice(extra_index, body_len);
  25424         extra_index += body.len;
  25425         if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison;
  25426 
  25427         const val = try sema.resolveGenericBody(block, ret_src, body, inst, .type, .{ .simple = .function_ret_ty });
  25428         const ty = val.toType();
  25429         break :blk ty;
  25430     } else if (extra.data.bits.has_ret_ty_ref) blk: {
  25431         const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  25432         extra_index += 1;
  25433         if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison;
  25434 
  25435         const ret_ty_air_ref = try sema.resolveInst(ret_ty_ref);
  25436         const ret_ty_val = try sema.resolveConstDefinedValue(block, ret_src, ret_ty_air_ref, .{ .simple = .function_ret_ty });
  25437         break :blk ret_ty_val.toType();
  25438     } else .void;
  25439 
  25440     const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: {
  25441         const x = sema.code.extra[extra_index];
  25442         extra_index += 1;
  25443         break :blk x;
  25444     } else 0;
  25445 
  25446     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
  25447     if (has_body) {
  25448         extra_index += extra.data.body_len;
  25449         src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
  25450     }
  25451 
  25452     const is_var_args = extra.data.bits.is_var_args;
  25453     const is_inferred_error = extra.data.bits.is_inferred_error;
  25454     const is_noinline = extra.data.bits.is_noinline;
  25455 
  25456     return sema.funcCommon(
  25457         block,
  25458         inst_data.src_node,
  25459         inst,
  25460         cc,
  25461         ret_ty,
  25462         is_var_args,
  25463         is_inferred_error,
  25464         has_body,
  25465         src_locs,
  25466         noalias_bits,
  25467         is_noinline,
  25468     );
  25469 }
  25470 
  25471 fn zirCUndef(
  25472     sema: *Sema,
  25473     block: *Block,
  25474     extended: Zir.Inst.Extended.InstData,
  25475 ) CompileError!Air.Inst.Ref {
  25476     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25477     const src = block.builtinCallArgSrc(extra.node, 0);
  25478 
  25479     const name = try sema.resolveConstString(block, src, extra.operand, .{ .simple = .operand_cUndef_macro_name });
  25480     try block.c_import_buf.?.print("#undef {s}\n", .{name});
  25481     return .void_value;
  25482 }
  25483 
  25484 fn zirCInclude(
  25485     sema: *Sema,
  25486     block: *Block,
  25487     extended: Zir.Inst.Extended.InstData,
  25488 ) CompileError!Air.Inst.Ref {
  25489     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25490     const src = block.builtinCallArgSrc(extra.node, 0);
  25491 
  25492     const name = try sema.resolveConstString(block, src, extra.operand, .{ .simple = .operand_cInclude_file_name });
  25493     try block.c_import_buf.?.print("#include <{s}>\n", .{name});
  25494     return .void_value;
  25495 }
  25496 
  25497 fn zirCDefine(
  25498     sema: *Sema,
  25499     block: *Block,
  25500     extended: Zir.Inst.Extended.InstData,
  25501 ) CompileError!Air.Inst.Ref {
  25502     const pt = sema.pt;
  25503     const zcu = pt.zcu;
  25504     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25505     const name_src = block.builtinCallArgSrc(extra.node, 0);
  25506     const val_src = block.builtinCallArgSrc(extra.node, 1);
  25507 
  25508     const name = try sema.resolveConstString(block, name_src, extra.lhs, .{ .simple = .operand_cDefine_macro_name });
  25509     const rhs = try sema.resolveInst(extra.rhs);
  25510     if (sema.typeOf(rhs).zigTypeTag(zcu) != .void) {
  25511         const value = try sema.resolveConstString(block, val_src, extra.rhs, .{ .simple = .operand_cDefine_macro_value });
  25512         try block.c_import_buf.?.print("#define {s} {s}\n", .{ name, value });
  25513     } else {
  25514         try block.c_import_buf.?.print("#define {s}\n", .{name});
  25515     }
  25516     return .void_value;
  25517 }
  25518 
  25519 fn zirWasmMemorySize(
  25520     sema: *Sema,
  25521     block: *Block,
  25522     extended: Zir.Inst.Extended.InstData,
  25523 ) CompileError!Air.Inst.Ref {
  25524     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25525     const index_src = block.builtinCallArgSrc(extra.node, 0);
  25526     const builtin_src = block.nodeOffset(extra.node);
  25527     const target = sema.pt.zcu.getTarget();
  25528     if (!target.cpu.arch.isWasm()) {
  25529         return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  25530     }
  25531 
  25532     const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.operand, .u32, .{ .simple = .wasm_memory_index }));
  25533     try sema.requireRuntimeBlock(block, builtin_src, null);
  25534     return block.addInst(.{
  25535         .tag = .wasm_memory_size,
  25536         .data = .{ .pl_op = .{
  25537             .operand = .none,
  25538             .payload = index,
  25539         } },
  25540     });
  25541 }
  25542 
  25543 fn zirWasmMemoryGrow(
  25544     sema: *Sema,
  25545     block: *Block,
  25546     extended: Zir.Inst.Extended.InstData,
  25547 ) CompileError!Air.Inst.Ref {
  25548     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25549     const builtin_src = block.nodeOffset(extra.node);
  25550     const index_src = block.builtinCallArgSrc(extra.node, 0);
  25551     const delta_src = block.builtinCallArgSrc(extra.node, 1);
  25552     const target = sema.pt.zcu.getTarget();
  25553     if (!target.cpu.arch.isWasm()) {
  25554         return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  25555     }
  25556 
  25557     const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.lhs, .u32, .{ .simple = .wasm_memory_index }));
  25558     const delta = try sema.coerce(block, .usize, try sema.resolveInst(extra.rhs), delta_src);
  25559 
  25560     try sema.requireRuntimeBlock(block, builtin_src, null);
  25561     return block.addInst(.{
  25562         .tag = .wasm_memory_grow,
  25563         .data = .{ .pl_op = .{
  25564             .operand = delta,
  25565             .payload = index,
  25566         } },
  25567     });
  25568 }
  25569 
  25570 fn resolvePrefetchOptions(
  25571     sema: *Sema,
  25572     block: *Block,
  25573     src: LazySrcLoc,
  25574     zir_ref: Zir.Inst.Ref,
  25575 ) CompileError!std.builtin.PrefetchOptions {
  25576     const pt = sema.pt;
  25577     const zcu = pt.zcu;
  25578     const gpa = sema.gpa;
  25579     const ip = &zcu.intern_pool;
  25580     const options_ty = try sema.getBuiltinType(src, .PrefetchOptions);
  25581     const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src);
  25582 
  25583     const rw_src = block.src(.{ .init_field_rw = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25584     const locality_src = block.src(.{ .init_field_locality = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25585     const cache_src = block.src(.{ .init_field_cache = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25586 
  25587     const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "rw", .no_embedded_nulls), rw_src);
  25588     const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{ .simple = .prefetch_options });
  25589 
  25590     const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "locality", .no_embedded_nulls), locality_src);
  25591     const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{ .simple = .prefetch_options });
  25592 
  25593     const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "cache", .no_embedded_nulls), cache_src);
  25594     const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{ .simple = .prefetch_options });
  25595 
  25596     return std.builtin.PrefetchOptions{
  25597         .rw = try sema.interpretBuiltinType(block, rw_src, rw_val, std.builtin.PrefetchOptions.Rw),
  25598         .locality = @intCast(try locality_val.toUnsignedIntSema(pt)),
  25599         .cache = try sema.interpretBuiltinType(block, cache_src, cache_val, std.builtin.PrefetchOptions.Cache),
  25600     };
  25601 }
  25602 
  25603 fn zirPrefetch(
  25604     sema: *Sema,
  25605     block: *Block,
  25606     extended: Zir.Inst.Extended.InstData,
  25607 ) CompileError!Air.Inst.Ref {
  25608     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25609     const ptr_src = block.builtinCallArgSrc(extra.node, 0);
  25610     const opts_src = block.builtinCallArgSrc(extra.node, 1);
  25611     const ptr = try sema.resolveInst(extra.lhs);
  25612     try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr));
  25613 
  25614     const options = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs);
  25615 
  25616     if (!block.isComptime()) {
  25617         _ = try block.addInst(.{
  25618             .tag = .prefetch,
  25619             .data = .{ .prefetch = .{
  25620                 .ptr = ptr,
  25621                 .rw = options.rw,
  25622                 .locality = options.locality,
  25623                 .cache = options.cache,
  25624             } },
  25625         });
  25626     }
  25627 
  25628     return .void_value;
  25629 }
  25630 
  25631 fn resolveExternOptions(
  25632     sema: *Sema,
  25633     block: *Block,
  25634     src: LazySrcLoc,
  25635     zir_ref: Zir.Inst.Ref,
  25636 ) CompileError!struct {
  25637     name: InternPool.NullTerminatedString,
  25638     library_name: InternPool.OptionalNullTerminatedString,
  25639     linkage: std.builtin.GlobalLinkage,
  25640     visibility: std.builtin.SymbolVisibility,
  25641     is_thread_local: bool,
  25642     is_dll_import: bool,
  25643     relocation: std.builtin.ExternOptions.Relocation,
  25644 } {
  25645     const pt = sema.pt;
  25646     const zcu = pt.zcu;
  25647     const gpa = sema.gpa;
  25648     const ip = &zcu.intern_pool;
  25649     const options_inst = try sema.resolveInst(zir_ref);
  25650     const extern_options_ty = try sema.getBuiltinType(src, .ExternOptions);
  25651     const options = try sema.coerce(block, extern_options_ty, options_inst, src);
  25652 
  25653     const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25654     const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25655     const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25656     const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25657     const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25658     const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25659     const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25660 
  25661     const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
  25662     const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });
  25663 
  25664     const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "library_name", .no_embedded_nulls), library_src);
  25665     const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ .simple = .extern_options });
  25666 
  25667     const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
  25668     const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options });
  25669     const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage);
  25670 
  25671     const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
  25672     const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_ref, .{ .simple = .extern_options });
  25673     const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility);
  25674 
  25675     const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src);
  25676     const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options });
  25677 
  25678     const library_name = if (library_name_val.optionalValue(zcu)) |library_name_payload| library_name: {
  25679         const library_name = try sema.toConstString(block, library_src, Air.internedToRef(library_name_payload.toIntern()), .{ .simple = .extern_options });
  25680         if (library_name.len == 0) {
  25681             return sema.fail(block, library_src, "library name cannot be empty", .{});
  25682         }
  25683         try sema.handleExternLibName(block, library_src, library_name);
  25684         break :library_name library_name;
  25685     } else null;
  25686 
  25687     const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
  25688     const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options });
  25689 
  25690     const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "relocation", .no_embedded_nulls), relocation_src);
  25691     const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });
  25692     const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation);
  25693 
  25694     if (name.len == 0) {
  25695         return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
  25696     }
  25697 
  25698     if (linkage != .weak and linkage != .strong) {
  25699         return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{});
  25700     }
  25701 
  25702     return .{
  25703         .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls),
  25704         .library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
  25705         .linkage = linkage,
  25706         .visibility = visibility,
  25707         .is_thread_local = is_thread_local_val.toBool(),
  25708         .is_dll_import = is_dll_import_val.toBool(),
  25709         .relocation = relocation,
  25710     };
  25711 }
  25712 
  25713 fn zirBuiltinExtern(
  25714     sema: *Sema,
  25715     block: *Block,
  25716     extended: Zir.Inst.Extended.InstData,
  25717 ) CompileError!Air.Inst.Ref {
  25718     const pt = sema.pt;
  25719     const zcu = pt.zcu;
  25720     const ip = &zcu.intern_pool;
  25721     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25722     const src = block.nodeOffset(extra.node);
  25723     const ty_src = block.builtinCallArgSrc(extra.node, 0);
  25724     const options_src = block.builtinCallArgSrc(extra.node, 1);
  25725 
  25726     var ty = try sema.resolveType(block, ty_src, extra.lhs);
  25727     if (!ty.isPtrAtRuntime(zcu)) {
  25728         return sema.fail(block, ty_src, "expected (optional) pointer", .{});
  25729     }
  25730     if (!try sema.validateExternType(ty, .other)) {
  25731         const msg = msg: {
  25732             const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ty.fmt(pt)});
  25733             errdefer msg.destroy(sema.gpa);
  25734             try sema.explainWhyTypeIsNotExtern(msg, ty_src, ty, .other);
  25735             break :msg msg;
  25736         };
  25737         return sema.failWithOwnedErrorMsg(block, msg);
  25738     }
  25739 
  25740     const options = try sema.resolveExternOptions(block, options_src, extra.rhs);
  25741     switch (options.linkage) {
  25742         .internal => if (options.visibility != .default) {
  25743             return sema.fail(block, options_src, "internal symbol cannot have non-default visibility", .{});
  25744         },
  25745         .strong, .weak => {},
  25746         .link_once => return sema.fail(block, options_src, "external symbol cannot have link once linkage", .{}),
  25747     }
  25748     switch (options.relocation) {
  25749         .any => {},
  25750         .pcrel => if (options.visibility == .default) return sema.fail(block, options_src, "cannot require a pc-relative relocation to a symbol with default visibility", .{}),
  25751     }
  25752 
  25753     // TODO: error for threadlocal functions, non-const functions, etc
  25754 
  25755     if (options.linkage == .weak and !ty.ptrAllowsZero(zcu)) {
  25756         ty = try pt.optionalType(ty.toIntern());
  25757     }
  25758     const ptr_info = ty.ptrInfo(zcu);
  25759 
  25760     const extern_val = try pt.getExtern(.{
  25761         .name = options.name,
  25762         .ty = ptr_info.child,
  25763         .lib_name = options.library_name,
  25764         .linkage = options.linkage,
  25765         .visibility = options.visibility,
  25766         .is_threadlocal = options.is_thread_local,
  25767         .is_dll_import = options.is_dll_import,
  25768         .relocation = options.relocation,
  25769         .is_const = ptr_info.flags.is_const,
  25770         .alignment = ptr_info.flags.alignment,
  25771         .@"addrspace" = ptr_info.flags.address_space,
  25772         // This instruction is just for source locations.
  25773         // `builtin_extern` doesn't provide enough information, and isn't currently tracked.
  25774         // So, for now, just use our containing `declaration`.
  25775         .zir_index = switch (sema.owner.unwrap()) {
  25776             .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,
  25777             .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?,
  25778             .memoized_state => unreachable,
  25779             .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
  25780             .func => |func| zir_index: {
  25781                 const func_info = zcu.funcInfo(func);
  25782                 const owner_func_info = if (func_info.generic_owner != .none) owner: {
  25783                     break :owner zcu.funcInfo(func_info.generic_owner);
  25784                 } else func_info;
  25785                 break :zir_index ip.getNav(owner_func_info.owner_nav).analysis.?.zir_index;
  25786             },
  25787         },
  25788         .owner_nav = undefined, // ignored by `getExtern`
  25789         .source = .builtin,
  25790     });
  25791 
  25792     const uncasted_ptr = try sema.analyzeNavRef(block, src, ip.indexToKey(extern_val).@"extern".owner_nav);
  25793     // We want to cast to `ty`, but that isn't necessarily an allowed coercion.
  25794     if (try sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| {
  25795         const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, ty);
  25796         return Air.internedToRef(casted_ptr_val.toIntern());
  25797     } else {
  25798         return block.addBitCast(ty, uncasted_ptr);
  25799     }
  25800 }
  25801 
  25802 fn zirWorkItem(
  25803     sema: *Sema,
  25804     block: *Block,
  25805     extended: Zir.Inst.Extended.InstData,
  25806     zir_tag: Zir.Inst.Extended,
  25807 ) CompileError!Air.Inst.Ref {
  25808     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25809     const dimension_src = block.builtinCallArgSrc(extra.node, 0);
  25810     const builtin_src = block.nodeOffset(extra.node);
  25811     const target = sema.pt.zcu.getTarget();
  25812 
  25813     switch (target.cpu.arch) {
  25814         // TODO: Allow for other GPU targets.
  25815         .amdgcn, .spirv64, .spirv32, .nvptx, .nvptx64 => {},
  25816         else => {
  25817             return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)});
  25818         },
  25819     }
  25820 
  25821     const dimension: u32 = @intCast(try sema.resolveInt(block, dimension_src, extra.operand, .u32, .{ .simple = .work_group_dim_index }));
  25822     try sema.requireRuntimeBlock(block, builtin_src, null);
  25823 
  25824     return block.addInst(.{
  25825         .tag = switch (zir_tag) {
  25826             .work_item_id => .work_item_id,
  25827             .work_group_size => .work_group_size,
  25828             .work_group_id => .work_group_id,
  25829             else => unreachable,
  25830         },
  25831         .data = .{ .pl_op = .{
  25832             .operand = .none,
  25833             .payload = dimension,
  25834         } },
  25835     });
  25836 }
  25837 
  25838 fn zirInComptime(
  25839     sema: *Sema,
  25840     block: *Block,
  25841 ) CompileError!Air.Inst.Ref {
  25842     _ = sema;
  25843     return if (block.isComptime()) .bool_true else .bool_false;
  25844 }
  25845 
  25846 fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  25847     const pt = sema.pt;
  25848     const zcu = pt.zcu;
  25849     const gpa = zcu.gpa;
  25850     const ip = &zcu.intern_pool;
  25851 
  25852     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  25853     const src = block.nodeOffset(src_node);
  25854     const value: Zir.Inst.BuiltinValue = @enumFromInt(extended.small);
  25855 
  25856     const ty = switch (value) {
  25857         // zig fmt: off
  25858         .atomic_order       => try sema.getBuiltinType(src, .AtomicOrder),
  25859         .atomic_rmw_op      => try sema.getBuiltinType(src, .AtomicRmwOp),
  25860         .calling_convention => try sema.getBuiltinType(src, .CallingConvention),
  25861         .address_space      => try sema.getBuiltinType(src, .AddressSpace),
  25862         .float_mode         => try sema.getBuiltinType(src, .FloatMode),
  25863         .reduce_op          => try sema.getBuiltinType(src, .ReduceOp),
  25864         .call_modifier      => try sema.getBuiltinType(src, .CallModifier),
  25865         .prefetch_options   => try sema.getBuiltinType(src, .PrefetchOptions),
  25866         .export_options     => try sema.getBuiltinType(src, .ExportOptions),
  25867         .extern_options     => try sema.getBuiltinType(src, .ExternOptions),
  25868         .type_info          => try sema.getBuiltinType(src, .Type),
  25869         .branch_hint        => try sema.getBuiltinType(src, .BranchHint),
  25870         .clobbers           => try sema.getBuiltinType(src, .@"assembly.Clobbers"),
  25871         // zig fmt: on
  25872 
  25873         // Values are handled here.
  25874         .calling_convention_c => {
  25875             const callconv_ty = try sema.getBuiltinType(src, .CallingConvention);
  25876             return try sema.namespaceLookupVal(
  25877                 block,
  25878                 src,
  25879                 callconv_ty.getNamespaceIndex(zcu),
  25880                 try ip.getOrPutString(gpa, pt.tid, "c", .no_embedded_nulls),
  25881             ) orelse @panic("std.builtin is corrupt");
  25882         },
  25883         .calling_convention_inline => {
  25884             comptime assert(@typeInfo(std.builtin.CallingConvention.Tag).@"enum".tag_type == u8);
  25885             const callconv_ty = try sema.getBuiltinType(src, .CallingConvention);
  25886             const callconv_tag_ty = callconv_ty.unionTagType(zcu) orelse @panic("std.builtin is corrupt");
  25887             const inline_tag_val = try pt.enumValue(
  25888                 callconv_tag_ty,
  25889                 (try pt.intValue(
  25890                     .u8,
  25891                     @intFromEnum(std.builtin.CallingConvention.@"inline"),
  25892                 )).toIntern(),
  25893             );
  25894             return sema.coerce(block, callconv_ty, Air.internedToRef(inline_tag_val.toIntern()), src);
  25895         },
  25896     };
  25897     return Air.internedToRef(ty.toIntern());
  25898 }
  25899 
  25900 fn zirInplaceArithResultTy(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  25901     const pt = sema.pt;
  25902     const zcu = pt.zcu;
  25903 
  25904     const lhs = try sema.resolveInst(@enumFromInt(extended.operand));
  25905     const lhs_ty = sema.typeOf(lhs);
  25906 
  25907     const op: Zir.Inst.InplaceOp = @enumFromInt(extended.small);
  25908     const ty: Type = switch (op) {
  25909         .add_eq => ty: {
  25910             const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty;
  25911             switch (ptr_size) {
  25912                 .one, .slice => break :ty lhs_ty, // invalid, let it error
  25913                 .many, .c => break :ty .usize, // `[*]T + usize`
  25914             }
  25915         },
  25916         .sub_eq => ty: {
  25917             const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty;
  25918             switch (ptr_size) {
  25919                 .one, .slice => break :ty lhs_ty, // invalid, let it error
  25920                 .many, .c => break :ty .generic_poison, // could be `[*]T - [*]T` or `[*]T - usize`
  25921             }
  25922         },
  25923     };
  25924     return Air.internedToRef(ty.toIntern());
  25925 }
  25926 
  25927 fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
  25928     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25929     const uncoerced_hint = try sema.resolveInst(extra.operand);
  25930     const operand_src = block.builtinCallArgSrc(extra.node, 0);
  25931 
  25932     const hint_ty = try sema.getBuiltinType(operand_src, .BranchHint);
  25933     const coerced_hint = try sema.coerce(block, hint_ty, uncoerced_hint, operand_src);
  25934     const hint_val = try sema.resolveConstDefinedValue(block, operand_src, coerced_hint, .{ .simple = .operand_branchHint });
  25935 
  25936     // We only apply the first hint in a branch.
  25937     // This allows user-provided hints to override implicit cold hints.
  25938     if (sema.branch_hint == null) {
  25939         sema.branch_hint = try sema.interpretBuiltinType(block, operand_src, hint_val, std.builtin.BranchHint);
  25940     }
  25941 }
  25942 
  25943 fn zirFloatOpResultType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  25944     const pt = sema.pt;
  25945     const zcu = pt.zcu;
  25946     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25947     const operand_src = block.builtinCallArgSrc(extra.node, 0);
  25948 
  25949     const raw_ty = try sema.resolveTypeOrPoison(block, operand_src, extra.operand) orelse return .generic_poison_type;
  25950     const float_ty = raw_ty.optEuBaseType(zcu);
  25951 
  25952     switch (float_ty.scalarType(zcu).zigTypeTag(zcu)) {
  25953         .float, .comptime_float => {},
  25954         else => return sema.fail(
  25955             block,
  25956             operand_src,
  25957             "expected vector of floats or float type, found '{f}'",
  25958             .{float_ty.fmt(sema.pt)},
  25959         ),
  25960     }
  25961 
  25962     return .fromType(float_ty);
  25963 }
  25964 
  25965 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
  25966     if (block.isComptime()) {
  25967         const msg, const fail_block = msg: {
  25968             const msg = try sema.errMsg(src, "unable to evaluate comptime expression", .{});
  25969             errdefer msg.destroy(sema.gpa);
  25970 
  25971             if (runtime_src) |some| {
  25972                 try sema.errNote(some, msg, "operation is runtime due to this operand", .{});
  25973             }
  25974 
  25975             const fail_block = try block.explainWhyBlockIsComptime(msg);
  25976 
  25977             break :msg .{ msg, fail_block };
  25978         };
  25979         return sema.failWithOwnedErrorMsg(fail_block, msg);
  25980     }
  25981 }
  25982 
  25983 /// Emit a compile error if type cannot be used for a runtime variable.
  25984 pub fn validateVarType(
  25985     sema: *Sema,
  25986     block: *Block,
  25987     src: LazySrcLoc,
  25988     var_ty: Type,
  25989     is_extern: bool,
  25990 ) CompileError!void {
  25991     const pt = sema.pt;
  25992     const zcu = pt.zcu;
  25993     if (is_extern) {
  25994         if (!try sema.validateExternType(var_ty, .other)) {
  25995             const msg = msg: {
  25996                 const msg = try sema.errMsg(src, "extern variable cannot have type '{f}'", .{var_ty.fmt(pt)});
  25997                 errdefer msg.destroy(sema.gpa);
  25998                 try sema.explainWhyTypeIsNotExtern(msg, src, var_ty, .other);
  25999                 break :msg msg;
  26000             };
  26001             return sema.failWithOwnedErrorMsg(block, msg);
  26002         }
  26003     } else {
  26004         if (var_ty.zigTypeTag(zcu) == .@"opaque") {
  26005             return sema.fail(
  26006                 block,
  26007                 src,
  26008                 "non-extern variable with opaque type '{f}'",
  26009                 .{var_ty.fmt(pt)},
  26010             );
  26011         }
  26012     }
  26013 
  26014     if (!try var_ty.comptimeOnlySema(pt)) return;
  26015 
  26016     const msg = msg: {
  26017         const msg = try sema.errMsg(src, "variable of type '{f}' must be const or comptime", .{var_ty.fmt(pt)});
  26018         errdefer msg.destroy(sema.gpa);
  26019 
  26020         try sema.explainWhyTypeIsComptime(msg, src, var_ty);
  26021         if (var_ty.zigTypeTag(zcu) == .comptime_int or var_ty.zigTypeTag(zcu) == .comptime_float) {
  26022             try sema.errNote(src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{});
  26023         }
  26024 
  26025         break :msg msg;
  26026     };
  26027     return sema.failWithOwnedErrorMsg(block, msg);
  26028 }
  26029 
  26030 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void);
  26031 
  26032 fn explainWhyTypeIsComptime(
  26033     sema: *Sema,
  26034     msg: *Zcu.ErrorMsg,
  26035     src_loc: LazySrcLoc,
  26036     ty: Type,
  26037 ) CompileError!void {
  26038     var type_set = TypeSet{};
  26039     defer type_set.deinit(sema.gpa);
  26040 
  26041     try ty.resolveFully(sema.pt);
  26042     return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set);
  26043 }
  26044 
  26045 fn explainWhyTypeIsComptimeInner(
  26046     sema: *Sema,
  26047     msg: *Zcu.ErrorMsg,
  26048     src_loc: LazySrcLoc,
  26049     ty: Type,
  26050     type_set: *TypeSet,
  26051 ) CompileError!void {
  26052     const pt = sema.pt;
  26053     const zcu = pt.zcu;
  26054     const ip = &zcu.intern_pool;
  26055     switch (ty.zigTypeTag(zcu)) {
  26056         .bool,
  26057         .int,
  26058         .float,
  26059         .error_set,
  26060         .@"enum",
  26061         .frame,
  26062         .@"anyframe",
  26063         .void,
  26064         => return,
  26065 
  26066         .@"fn" => {
  26067             try sema.errNote(src_loc, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)});
  26068         },
  26069 
  26070         .type => {
  26071             try sema.errNote(src_loc, msg, "types are not available at runtime", .{});
  26072         },
  26073 
  26074         .comptime_float,
  26075         .comptime_int,
  26076         .enum_literal,
  26077         .noreturn,
  26078         .undefined,
  26079         .null,
  26080         => return,
  26081 
  26082         .@"opaque" => {
  26083             try sema.errNote(src_loc, msg, "opaque type '{f}' has undefined size", .{ty.fmt(pt)});
  26084         },
  26085 
  26086         .array, .vector => {
  26087             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set);
  26088         },
  26089         .pointer => {
  26090             const elem_ty = ty.elemType2(zcu);
  26091             if (elem_ty.zigTypeTag(zcu) == .@"fn") {
  26092                 const fn_info = zcu.typeToFunc(elem_ty).?;
  26093                 if (fn_info.is_generic) {
  26094                     try sema.errNote(src_loc, msg, "function is generic", .{});
  26095                 }
  26096                 switch (fn_info.cc) {
  26097                     .@"inline" => try sema.errNote(src_loc, msg, "function has inline calling convention", .{}),
  26098                     else => {},
  26099                 }
  26100                 if (Type.fromInterned(fn_info.return_type).comptimeOnly(zcu)) {
  26101                     try sema.errNote(src_loc, msg, "function has a comptime-only return type", .{});
  26102                 }
  26103                 return;
  26104             }
  26105             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set);
  26106         },
  26107 
  26108         .optional => {
  26109             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(zcu), type_set);
  26110         },
  26111         .error_union => {
  26112             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(zcu), type_set);
  26113         },
  26114 
  26115         .@"struct" => {
  26116             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  26117 
  26118             if (zcu.typeToStruct(ty)) |struct_type| {
  26119                 for (0..struct_type.field_types.len) |i| {
  26120                     const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  26121                     const field_src: LazySrcLoc = .{
  26122                         .base_node_inst = struct_type.zir_index,
  26123                         .offset = .{ .container_field_type = @intCast(i) },
  26124                     };
  26125 
  26126                     if (try field_ty.comptimeOnlySema(pt)) {
  26127                         try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{});
  26128                         try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set);
  26129                     }
  26130                 }
  26131             }
  26132             // TODO tuples
  26133         },
  26134 
  26135         .@"union" => {
  26136             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  26137 
  26138             if (zcu.typeToUnion(ty)) |union_obj| {
  26139                 for (0..union_obj.field_types.len) |i| {
  26140                     const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]);
  26141                     const field_src: LazySrcLoc = .{
  26142                         .base_node_inst = union_obj.zir_index,
  26143                         .offset = .{ .container_field_type = @intCast(i) },
  26144                     };
  26145 
  26146                     if (try field_ty.comptimeOnlySema(pt)) {
  26147                         try sema.errNote(field_src, msg, "union requires comptime because of this field", .{});
  26148                         try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set);
  26149                     }
  26150                 }
  26151             }
  26152         },
  26153     }
  26154 }
  26155 
  26156 const ExternPosition = enum {
  26157     ret_ty,
  26158     param_ty,
  26159     union_field,
  26160     struct_field,
  26161     element,
  26162     other,
  26163 };
  26164 
  26165 /// Returns true if `ty` is allowed in extern types.
  26166 /// Does *NOT* require `ty` to be resolved in any way.
  26167 /// Calls `resolveLayout` for packed containers.
  26168 fn validateExternType(
  26169     sema: *Sema,
  26170     ty: Type,
  26171     position: ExternPosition,
  26172 ) !bool {
  26173     const pt = sema.pt;
  26174     const zcu = pt.zcu;
  26175     switch (ty.zigTypeTag(zcu)) {
  26176         .type,
  26177         .comptime_float,
  26178         .comptime_int,
  26179         .enum_literal,
  26180         .undefined,
  26181         .null,
  26182         .error_union,
  26183         .error_set,
  26184         .frame,
  26185         => return false,
  26186         .void => return position == .union_field or position == .ret_ty or position == .struct_field or position == .element,
  26187         .noreturn => return position == .ret_ty,
  26188         .@"opaque",
  26189         .bool,
  26190         .float,
  26191         .@"anyframe",
  26192         => return true,
  26193         .pointer => {
  26194             if (ty.childType(zcu).zigTypeTag(zcu) == .@"fn") {
  26195                 return ty.isConstPtr(zcu) and try sema.validateExternType(ty.childType(zcu), .other);
  26196             }
  26197             return !(ty.isSlice(zcu) or try ty.comptimeOnlySema(pt));
  26198         },
  26199         .int => switch (ty.intInfo(zcu).bits) {
  26200             0, 8, 16, 32, 64, 128 => return true,
  26201             else => return false,
  26202         },
  26203         .@"fn" => {
  26204             if (position != .other) return false;
  26205             // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI.
  26206             // The goal is to experiment with more integrated CPU/GPU code.
  26207             if (ty.fnCallingConvention(zcu) == .nvptx_kernel) {
  26208                 return true;
  26209             }
  26210             return !target_util.fnCallConvAllowsZigTypes(ty.fnCallingConvention(zcu));
  26211         },
  26212         .@"enum" => {
  26213             return sema.validateExternType(ty.intTagType(zcu), position);
  26214         },
  26215         .@"struct", .@"union" => switch (ty.containerLayout(zcu)) {
  26216             .@"extern" => return true,
  26217             .@"packed" => {
  26218                 const bit_size = try ty.bitSizeSema(pt);
  26219                 switch (bit_size) {
  26220                     0, 8, 16, 32, 64, 128 => return true,
  26221                     else => return false,
  26222                 }
  26223             },
  26224             .auto => return !(try ty.hasRuntimeBitsSema(pt)),
  26225         },
  26226         .array => {
  26227             if (position == .ret_ty or position == .param_ty) return false;
  26228             return sema.validateExternType(ty.elemType2(zcu), .element);
  26229         },
  26230         .vector => return sema.validateExternType(ty.elemType2(zcu), .element),
  26231         .optional => return ty.isPtrLikeOptional(zcu),
  26232     }
  26233 }
  26234 
  26235 fn explainWhyTypeIsNotExtern(
  26236     sema: *Sema,
  26237     msg: *Zcu.ErrorMsg,
  26238     src_loc: LazySrcLoc,
  26239     ty: Type,
  26240     position: ExternPosition,
  26241 ) CompileError!void {
  26242     const pt = sema.pt;
  26243     const zcu = pt.zcu;
  26244     switch (ty.zigTypeTag(zcu)) {
  26245         .@"opaque",
  26246         .bool,
  26247         .float,
  26248         .@"anyframe",
  26249         => return,
  26250 
  26251         .type,
  26252         .comptime_float,
  26253         .comptime_int,
  26254         .enum_literal,
  26255         .undefined,
  26256         .null,
  26257         .error_union,
  26258         .error_set,
  26259         .frame,
  26260         => return,
  26261 
  26262         .pointer => {
  26263             if (ty.isSlice(zcu)) {
  26264                 try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{});
  26265             } else {
  26266                 const pointee_ty = ty.childType(zcu);
  26267                 if (!ty.isConstPtr(zcu) and pointee_ty.zigTypeTag(zcu) == .@"fn") {
  26268                     try sema.errNote(src_loc, msg, "pointer to extern function must be 'const'", .{});
  26269                 } else if (try ty.comptimeOnlySema(pt)) {
  26270                     try sema.errNote(src_loc, msg, "pointer to comptime-only type '{f}'", .{pointee_ty.fmt(pt)});
  26271                     try sema.explainWhyTypeIsComptime(msg, src_loc, ty);
  26272                 }
  26273                 try sema.explainWhyTypeIsNotExtern(msg, src_loc, pointee_ty, .other);
  26274             }
  26275         },
  26276         .void => try sema.errNote(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}),
  26277         .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
  26278         .int => if (!std.math.isPowerOfTwo(ty.intInfo(zcu).bits)) {
  26279             try sema.errNote(src_loc, msg, "only integers with 0 or power of two bits are extern compatible", .{});
  26280         } else {
  26281             try sema.errNote(src_loc, msg, "only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible", .{});
  26282         },
  26283         .@"fn" => {
  26284             if (position != .other) {
  26285                 try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  26286                 try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  26287                 return;
  26288             }
  26289             switch (ty.fnCallingConvention(zcu)) {
  26290                 .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}),
  26291                 .async => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}),
  26292                 .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}),
  26293                 else => return,
  26294             }
  26295         },
  26296         .@"enum" => {
  26297             const tag_ty = ty.intTagType(zcu);
  26298             try sema.errNote(src_loc, msg, "enum tag type '{f}' is not extern compatible", .{tag_ty.fmt(pt)});
  26299             try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
  26300         },
  26301         .@"struct" => try sema.errNote(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}),
  26302         .@"union" => try sema.errNote(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}),
  26303         .array => {
  26304             if (position == .ret_ty) {
  26305                 return sema.errNote(src_loc, msg, "arrays are not allowed as a return type", .{});
  26306             } else if (position == .param_ty) {
  26307                 return sema.errNote(src_loc, msg, "arrays are not allowed as a parameter type", .{});
  26308             }
  26309             try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element);
  26310         },
  26311         .vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element),
  26312         .optional => try sema.errNote(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
  26313     }
  26314 }
  26315 
  26316 /// Returns true if `ty` is allowed in packed types.
  26317 /// Does not require `ty` to be resolved in any way, but may resolve whether it is comptime-only.
  26318 fn validatePackedType(sema: *Sema, ty: Type) !bool {
  26319     const pt = sema.pt;
  26320     const zcu = pt.zcu;
  26321     return switch (ty.zigTypeTag(zcu)) {
  26322         .type,
  26323         .comptime_float,
  26324         .comptime_int,
  26325         .enum_literal,
  26326         .undefined,
  26327         .null,
  26328         .error_union,
  26329         .error_set,
  26330         .frame,
  26331         .noreturn,
  26332         .@"opaque",
  26333         .@"anyframe",
  26334         .@"fn",
  26335         .array,
  26336         => false,
  26337         .optional => return ty.isPtrLikeOptional(zcu),
  26338         .void,
  26339         .bool,
  26340         .float,
  26341         .int,
  26342         .vector,
  26343         => true,
  26344         .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).tag_mode) {
  26345             .auto => false,
  26346             .explicit, .nonexhaustive => true,
  26347         },
  26348         .pointer => !ty.isSlice(zcu) and !try ty.comptimeOnlySema(pt),
  26349         .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
  26350     };
  26351 }
  26352 
  26353 fn explainWhyTypeIsNotPacked(
  26354     sema: *Sema,
  26355     msg: *Zcu.ErrorMsg,
  26356     src_loc: LazySrcLoc,
  26357     ty: Type,
  26358 ) CompileError!void {
  26359     const pt = sema.pt;
  26360     const zcu = pt.zcu;
  26361     switch (ty.zigTypeTag(zcu)) {
  26362         .void,
  26363         .bool,
  26364         .float,
  26365         .int,
  26366         .vector,
  26367         .@"enum",
  26368         => return,
  26369         .type,
  26370         .comptime_float,
  26371         .comptime_int,
  26372         .enum_literal,
  26373         .undefined,
  26374         .null,
  26375         .frame,
  26376         .noreturn,
  26377         .@"opaque",
  26378         .error_union,
  26379         .error_set,
  26380         .@"anyframe",
  26381         .optional,
  26382         .array,
  26383         => try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}),
  26384         .pointer => if (ty.isSlice(zcu)) {
  26385             try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{});
  26386         } else {
  26387             try sema.errNote(src_loc, msg, "comptime-only pointer has no guaranteed in-memory representation", .{});
  26388             try sema.explainWhyTypeIsComptime(msg, src_loc, ty);
  26389         },
  26390         .@"fn" => {
  26391             try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  26392             try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  26393         },
  26394         .@"struct" => try sema.errNote(src_loc, msg, "only packed structs layout are allowed in packed types", .{}),
  26395         .@"union" => try sema.errNote(src_loc, msg, "only packed unions layout are allowed in packed types", .{}),
  26396     }
  26397 }
  26398 
  26399 /// Backends depend on panic decls being available when lowering safety-checked
  26400 /// instructions. This function ensures the panic function will be available to
  26401 /// be called during that time.
  26402 fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !void {
  26403     const zcu = sema.pt.zcu;
  26404 
  26405     // If the backend doesn't support `.panic_fn`, it doesn't want us to lower the panic handlers.
  26406     // The backend will transform panics into traps instead.
  26407     if (!zcu.backendSupportsFeature(.panic_fn)) return;
  26408 
  26409     const fn_index = try sema.getPanicIdFunc(src, panic_id);
  26410     const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(fn_index);
  26411     try sema.addReferenceEntry(null, src, .wrap(.{ .func = orig_fn_index }));
  26412     try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
  26413 }
  26414 
  26415 fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index {
  26416     const zcu = sema.pt.zcu;
  26417     try sema.ensureMemoizedStateResolved(src, .panic);
  26418     const panic_fn_index = zcu.builtin_decl_values.get(panic_id.toBuiltin());
  26419     switch (sema.owner.unwrap()) {
  26420         .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
  26421         .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(owner_func, true),
  26422     }
  26423     return panic_fn_index;
  26424 }
  26425 
  26426 fn addSafetyCheck(
  26427     sema: *Sema,
  26428     parent_block: *Block,
  26429     src: LazySrcLoc,
  26430     ok: Air.Inst.Ref,
  26431     panic_id: Zcu.SimplePanicId,
  26432 ) !void {
  26433     const gpa = sema.gpa;
  26434     assert(!parent_block.isComptime());
  26435 
  26436     var fail_block: Block = .{
  26437         .parent = parent_block,
  26438         .sema = sema,
  26439         .namespace = parent_block.namespace,
  26440         .instructions = .{},
  26441         .inlining = parent_block.inlining,
  26442         .comptime_reason = null,
  26443         .src_base_inst = parent_block.src_base_inst,
  26444         .type_name_ctx = parent_block.type_name_ctx,
  26445     };
  26446 
  26447     defer fail_block.instructions.deinit(gpa);
  26448 
  26449     try sema.safetyPanic(&fail_block, src, panic_id);
  26450     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  26451 }
  26452 
  26453 fn addSafetyCheckExtra(
  26454     sema: *Sema,
  26455     parent_block: *Block,
  26456     ok: Air.Inst.Ref,
  26457     fail_block: *Block,
  26458 ) !void {
  26459     const gpa = sema.gpa;
  26460 
  26461     try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
  26462 
  26463     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
  26464         1 + // The main block only needs space for the cond_br.
  26465         @typeInfo(Air.CondBr).@"struct".fields.len +
  26466         1 + // The ok branch of the cond_br only needs space for the br.
  26467         fail_block.instructions.items.len);
  26468 
  26469     try sema.air_instructions.ensureUnusedCapacity(gpa, 3);
  26470     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  26471     const cond_br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1);
  26472     const br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(cond_br_inst) + 1);
  26473     sema.air_instructions.appendAssumeCapacity(.{
  26474         .tag = .block,
  26475         .data = .{ .ty_pl = .{
  26476             .ty = .void_type,
  26477             .payload = sema.addExtraAssumeCapacity(Air.Block{
  26478                 .body_len = 1,
  26479             }),
  26480         } },
  26481     });
  26482     sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst));
  26483 
  26484     sema.air_instructions.appendAssumeCapacity(.{
  26485         .tag = .cond_br,
  26486         .data = .{
  26487             .pl_op = .{
  26488                 .operand = ok,
  26489                 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  26490                     .then_body_len = 1,
  26491                     .else_body_len = @intCast(fail_block.instructions.items.len),
  26492                     .branch_hints = .{
  26493                         // Safety check failure branch is cold.
  26494                         .true = .likely,
  26495                         .false = .cold,
  26496                         // Code coverage not wanted for panic branches.
  26497                         .then_cov = .none,
  26498                         .else_cov = .none,
  26499                     },
  26500                 }),
  26501             },
  26502         },
  26503     });
  26504     sema.air_extra.appendAssumeCapacity(@intFromEnum(br_inst));
  26505     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(fail_block.instructions.items));
  26506 
  26507     sema.air_instructions.appendAssumeCapacity(.{
  26508         .tag = .br,
  26509         .data = .{ .br = .{
  26510             .block_inst = block_inst,
  26511             .operand = .void_value,
  26512         } },
  26513     });
  26514 
  26515     parent_block.instructions.appendAssumeCapacity(block_inst);
  26516 }
  26517 
  26518 fn addSafetyCheckUnwrapError(
  26519     sema: *Sema,
  26520     parent_block: *Block,
  26521     src: LazySrcLoc,
  26522     operand: Air.Inst.Ref,
  26523     unwrap_err_tag: Air.Inst.Tag,
  26524     is_non_err_tag: Air.Inst.Tag,
  26525 ) !void {
  26526     assert(!parent_block.isComptime());
  26527     const ok = try parent_block.addUnOp(is_non_err_tag, operand);
  26528     const gpa = sema.gpa;
  26529 
  26530     var fail_block: Block = .{
  26531         .parent = parent_block,
  26532         .sema = sema,
  26533         .namespace = parent_block.namespace,
  26534         .instructions = .{},
  26535         .inlining = parent_block.inlining,
  26536         .comptime_reason = null,
  26537         .src_base_inst = parent_block.src_base_inst,
  26538         .type_name_ctx = parent_block.type_name_ctx,
  26539     };
  26540 
  26541     defer fail_block.instructions.deinit(gpa);
  26542 
  26543     const err = try fail_block.addTyOp(unwrap_err_tag, .anyerror, operand);
  26544     try safetyPanicUnwrapError(sema, &fail_block, src, err);
  26545 
  26546     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  26547 }
  26548 
  26549 fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air.Inst.Ref) !void {
  26550     const pt = sema.pt;
  26551     const zcu = pt.zcu;
  26552     if (!zcu.backendSupportsFeature(.panic_fn)) {
  26553         _ = try block.addNoOp(.trap);
  26554     } else {
  26555         const panic_fn = try getBuiltin(sema, src, .@"panic.unwrapError");
  26556         try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{err}, .@"safety check");
  26557     }
  26558 }
  26559 
  26560 fn addSafetyCheckIndexOob(
  26561     sema: *Sema,
  26562     parent_block: *Block,
  26563     src: LazySrcLoc,
  26564     index: Air.Inst.Ref,
  26565     len: Air.Inst.Ref,
  26566     cmp_op: Air.Inst.Tag,
  26567 ) !void {
  26568     assert(!parent_block.isComptime());
  26569     const ok = try parent_block.addBinOp(cmp_op, index, len);
  26570     return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.outOfBounds", &.{ index, len });
  26571 }
  26572 
  26573 fn addSafetyCheckInactiveUnionField(
  26574     sema: *Sema,
  26575     parent_block: *Block,
  26576     src: LazySrcLoc,
  26577     active_tag: Air.Inst.Ref,
  26578     wanted_tag: Air.Inst.Ref,
  26579 ) !void {
  26580     assert(!parent_block.isComptime());
  26581     const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
  26582     return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.inactiveUnionField", &.{ active_tag, wanted_tag });
  26583 }
  26584 
  26585 fn addSafetyCheckSentinelMismatch(
  26586     sema: *Sema,
  26587     parent_block: *Block,
  26588     src: LazySrcLoc,
  26589     maybe_sentinel: ?Value,
  26590     sentinel_ty: Type,
  26591     ptr: Air.Inst.Ref,
  26592     sentinel_index: Air.Inst.Ref,
  26593 ) !void {
  26594     assert(!parent_block.isComptime());
  26595     const pt = sema.pt;
  26596     const zcu = pt.zcu;
  26597     const expected_sentinel_val = maybe_sentinel orelse return;
  26598     const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern());
  26599 
  26600     const ptr_ty = sema.typeOf(ptr);
  26601     const actual_sentinel = if (ptr_ty.isSlice(zcu))
  26602         try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
  26603     else blk: {
  26604         const elem_ptr_ty = try ptr_ty.elemPtrType(null, pt);
  26605         const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
  26606         break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
  26607     };
  26608 
  26609     const ok = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: {
  26610         const eql = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq);
  26611         break :ok try parent_block.addReduce(eql, .And);
  26612     } else ok: {
  26613         assert(sentinel_ty.isSelfComparable(zcu, true));
  26614         break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
  26615     };
  26616 
  26617     return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{
  26618         expected_sentinel, actual_sentinel,
  26619     });
  26620 }
  26621 
  26622 fn addSafetyCheckCall(
  26623     sema: *Sema,
  26624     parent_block: *Block,
  26625     src: LazySrcLoc,
  26626     ok: Air.Inst.Ref,
  26627     comptime func_decl: Zcu.BuiltinDecl,
  26628     args: []const Air.Inst.Ref,
  26629 ) !void {
  26630     assert(!parent_block.isComptime());
  26631     const gpa = sema.gpa;
  26632     const pt = sema.pt;
  26633     const zcu = pt.zcu;
  26634 
  26635     var fail_block: Block = .{
  26636         .parent = parent_block,
  26637         .sema = sema,
  26638         .namespace = parent_block.namespace,
  26639         .instructions = .{},
  26640         .inlining = parent_block.inlining,
  26641         .comptime_reason = null,
  26642         .src_base_inst = parent_block.src_base_inst,
  26643         .type_name_ctx = parent_block.type_name_ctx,
  26644     };
  26645 
  26646     defer fail_block.instructions.deinit(gpa);
  26647 
  26648     if (!zcu.backendSupportsFeature(.panic_fn)) {
  26649         _ = try fail_block.addNoOp(.trap);
  26650     } else {
  26651         const panic_fn = try getBuiltin(sema, src, func_decl);
  26652         try sema.callBuiltin(&fail_block, src, Air.internedToRef(panic_fn), .auto, args, .@"safety check");
  26653     }
  26654 
  26655     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  26656 }
  26657 
  26658 /// This does not set `sema.branch_hint`.
  26659 fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) CompileError!void {
  26660     if (!sema.pt.zcu.backendSupportsFeature(.panic_fn)) {
  26661         _ = try block.addNoOp(.trap);
  26662     } else {
  26663         const panic_fn = try sema.getPanicIdFunc(src, panic_id);
  26664         try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{}, .@"safety check");
  26665     }
  26666 }
  26667 
  26668 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
  26669     sema.branch_count += 1;
  26670     if (sema.branch_count > sema.branch_quota) {
  26671         const msg = try sema.errMsg(
  26672             src,
  26673             "evaluation exceeded {d} backwards branches",
  26674             .{sema.branch_quota},
  26675         );
  26676         try sema.errNote(
  26677             src,
  26678             msg,
  26679             "use @setEvalBranchQuota() to raise the branch limit from {d}",
  26680             .{sema.branch_quota},
  26681         );
  26682         return sema.failWithOwnedErrorMsg(block, msg);
  26683     }
  26684 }
  26685 
  26686 fn fieldPtrLoad(
  26687     sema: *Sema,
  26688     block: *Block,
  26689     src: LazySrcLoc,
  26690     object_ptr: Air.Inst.Ref,
  26691     field_name: InternPool.NullTerminatedString,
  26692     field_name_src: LazySrcLoc,
  26693 ) CompileError!Air.Inst.Ref {
  26694     const pt = sema.pt;
  26695     const zcu = pt.zcu;
  26696     const object_ptr_ty = sema.typeOf(object_ptr);
  26697     const pointee_ty = object_ptr_ty.childType(zcu);
  26698     if (try typeHasOnePossibleValue(sema, pointee_ty)) |opv| {
  26699         const object: Air.Inst.Ref = .fromValue(opv);
  26700         return fieldVal(sema, block, src, object, field_name, field_name_src);
  26701     }
  26702 
  26703     if (try sema.resolveDefinedValue(block, src, object_ptr)) |object_ptr_val| {
  26704         if (try sema.pointerDeref(block, src, object_ptr_val, object_ptr_ty)) |object_val| {
  26705             const object: Air.Inst.Ref = .fromValue(object_val);
  26706             return fieldVal(sema, block, src, object, field_name, field_name_src);
  26707         }
  26708     }
  26709     const field_ptr = try sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
  26710     return analyzeLoad(sema, block, src, field_ptr, field_name_src);
  26711 }
  26712 
  26713 fn fieldVal(
  26714     sema: *Sema,
  26715     block: *Block,
  26716     src: LazySrcLoc,
  26717     object: Air.Inst.Ref,
  26718     field_name: InternPool.NullTerminatedString,
  26719     field_name_src: LazySrcLoc,
  26720 ) CompileError!Air.Inst.Ref {
  26721     // When editing this function, note that there is corresponding logic to be edited
  26722     // in `fieldPtr`. This function takes a value and returns a value.
  26723 
  26724     const pt = sema.pt;
  26725     const zcu = pt.zcu;
  26726     const ip = &zcu.intern_pool;
  26727     const object_src = src; // TODO better source location
  26728     const object_ty = sema.typeOf(object);
  26729 
  26730     // Zig allows dereferencing a single pointer during field lookup. Note that
  26731     // we don't actually need to generate the dereference some field lookups, like the
  26732     // length of arrays and other comptime operations.
  26733     const is_pointer_to = object_ty.isSinglePointer(zcu);
  26734 
  26735     const inner_ty = if (is_pointer_to)
  26736         object_ty.childType(zcu)
  26737     else
  26738         object_ty;
  26739 
  26740     switch (inner_ty.zigTypeTag(zcu)) {
  26741         .array => {
  26742             if (field_name.eqlSlice("len", ip)) {
  26743                 return Air.internedToRef((try pt.intValue(.usize, inner_ty.arrayLen(zcu))).toIntern());
  26744             } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
  26745                 const ptr_info = object_ty.ptrInfo(zcu);
  26746                 const result_ty = try pt.ptrTypeSema(.{
  26747                     .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(),
  26748                     .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  26749                     .flags = .{
  26750                         .size = .many,
  26751                         .alignment = ptr_info.flags.alignment,
  26752                         .is_const = ptr_info.flags.is_const,
  26753                         .is_volatile = ptr_info.flags.is_volatile,
  26754                         .is_allowzero = ptr_info.flags.is_allowzero,
  26755                         .address_space = ptr_info.flags.address_space,
  26756                         .vector_index = ptr_info.flags.vector_index,
  26757                     },
  26758                     .packed_offset = ptr_info.packed_offset,
  26759                 });
  26760                 return sema.coerce(block, result_ty, object, src);
  26761             } else {
  26762                 return sema.fail(
  26763                     block,
  26764                     field_name_src,
  26765                     "no member named '{f}' in '{f}'",
  26766                     .{ field_name.fmt(ip), object_ty.fmt(pt) },
  26767                 );
  26768             }
  26769         },
  26770         .pointer => {
  26771             const ptr_info = inner_ty.ptrInfo(zcu);
  26772             if (ptr_info.flags.size == .slice) {
  26773                 if (field_name.eqlSlice("ptr", ip)) {
  26774                     const slice = if (is_pointer_to)
  26775                         try sema.analyzeLoad(block, src, object, object_src)
  26776                     else
  26777                         object;
  26778                     return sema.analyzeSlicePtr(block, object_src, slice, inner_ty);
  26779                 } else if (field_name.eqlSlice("len", ip)) {
  26780                     const slice = if (is_pointer_to)
  26781                         try sema.analyzeLoad(block, src, object, object_src)
  26782                     else
  26783                         object;
  26784                     return sema.analyzeSliceLen(block, src, slice);
  26785                 } else {
  26786                     return sema.fail(
  26787                         block,
  26788                         field_name_src,
  26789                         "no member named '{f}' in '{f}'",
  26790                         .{ field_name.fmt(ip), object_ty.fmt(pt) },
  26791                     );
  26792                 }
  26793             }
  26794         },
  26795         .type => {
  26796             const dereffed_type = if (is_pointer_to)
  26797                 try sema.analyzeLoad(block, src, object, object_src)
  26798             else
  26799                 object;
  26800 
  26801             const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
  26802             const child_type = val.toType();
  26803 
  26804             switch (child_type.zigTypeTag(zcu)) {
  26805                 .error_set => {
  26806                     switch (ip.indexToKey(child_type.toIntern())) {
  26807                         .error_set_type => |error_set_type| blk: {
  26808                             if (error_set_type.nameIndex(ip, field_name) != null) break :blk;
  26809                             return sema.fail(block, src, "no error named '{f}' in '{f}'", .{
  26810                                 field_name.fmt(ip), child_type.fmt(pt),
  26811                             });
  26812                         },
  26813                         .inferred_error_set_type => {
  26814                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  26815                         },
  26816                         .simple_type => |t| {
  26817                             assert(t == .anyerror);
  26818                             _ = try pt.getErrorValue(field_name);
  26819                         },
  26820                         else => unreachable,
  26821                     }
  26822 
  26823                     const error_set_type = if (!child_type.isAnyError(zcu))
  26824                         child_type
  26825                     else
  26826                         try pt.singleErrorSetType(field_name);
  26827                     return Air.internedToRef((try pt.intern(.{ .err = .{
  26828                         .ty = error_set_type.toIntern(),
  26829                         .name = field_name,
  26830                     } })));
  26831                 },
  26832                 .@"union" => {
  26833                     if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  26834                         return inst;
  26835                     }
  26836                     try child_type.resolveFields(pt);
  26837                     if (child_type.unionTagType(zcu)) |enum_ty| {
  26838                         if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| {
  26839                             const field_index: u32 = @intCast(field_index_usize);
  26840                             return Air.internedToRef((try pt.enumValueFieldIndex(enum_ty, field_index)).toIntern());
  26841                         }
  26842                     }
  26843                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26844                 },
  26845                 .@"enum" => {
  26846                     if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  26847                         return inst;
  26848                     }
  26849                     const field_index_usize = child_type.enumFieldIndex(field_name, zcu) orelse
  26850                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26851                     const field_index: u32 = @intCast(field_index_usize);
  26852                     const enum_val = try pt.enumValueFieldIndex(child_type, field_index);
  26853                     return Air.internedToRef(enum_val.toIntern());
  26854                 },
  26855                 .@"struct", .@"opaque" => {
  26856                     if (!child_type.isTuple(zcu) and child_type.toIntern() != .anyopaque_type) {
  26857                         if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  26858                             return inst;
  26859                         }
  26860                     }
  26861                     return sema.failWithBadMemberAccess(block, child_type, src, field_name);
  26862                 },
  26863                 else => return sema.failWithOwnedErrorMsg(block, msg: {
  26864                     const msg = try sema.errMsg(src, "type '{f}' has no members", .{child_type.fmt(pt)});
  26865                     errdefer msg.destroy(sema.gpa);
  26866                     if (child_type.isSlice(zcu)) try sema.errNote(src, msg, "slice values have 'len' and 'ptr' members", .{});
  26867                     if (child_type.zigTypeTag(zcu) == .array) try sema.errNote(src, msg, "array values have 'len' member", .{});
  26868                     break :msg msg;
  26869                 }),
  26870             }
  26871         },
  26872         .@"struct" => if (is_pointer_to) {
  26873             // Avoid loading the entire struct by fetching a pointer and loading that
  26874             const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  26875             return sema.analyzeLoad(block, src, field_ptr, object_src);
  26876         } else {
  26877             return sema.structFieldVal(block, object, field_name, field_name_src, inner_ty);
  26878         },
  26879         .@"union" => if (is_pointer_to) {
  26880             // Avoid loading the entire union by fetching a pointer and loading that
  26881             const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  26882             return sema.analyzeLoad(block, src, field_ptr, object_src);
  26883         } else {
  26884             return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
  26885         },
  26886         else => {},
  26887     }
  26888     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  26889 }
  26890 
  26891 fn fieldPtr(
  26892     sema: *Sema,
  26893     block: *Block,
  26894     src: LazySrcLoc,
  26895     object_ptr: Air.Inst.Ref,
  26896     field_name: InternPool.NullTerminatedString,
  26897     field_name_src: LazySrcLoc,
  26898     initializing: bool,
  26899 ) CompileError!Air.Inst.Ref {
  26900     // When editing this function, note that there is corresponding logic to be edited
  26901     // in `fieldVal`. This function takes a pointer and returns a pointer.
  26902 
  26903     const pt = sema.pt;
  26904     const zcu = pt.zcu;
  26905     const ip = &zcu.intern_pool;
  26906     const object_ptr_src = src; // TODO better source location
  26907     const object_ptr_ty = sema.typeOf(object_ptr);
  26908     const object_ty = switch (object_ptr_ty.zigTypeTag(zcu)) {
  26909         .pointer => object_ptr_ty.childType(zcu),
  26910         else => return sema.fail(block, object_ptr_src, "expected pointer, found '{f}'", .{object_ptr_ty.fmt(pt)}),
  26911     };
  26912 
  26913     // Zig allows dereferencing a single pointer during field lookup. Note that
  26914     // we don't actually need to generate the dereference some field lookups, like the
  26915     // length of arrays and other comptime operations.
  26916     const is_pointer_to = object_ty.isSinglePointer(zcu);
  26917 
  26918     const inner_ty = if (is_pointer_to)
  26919         object_ty.childType(zcu)
  26920     else
  26921         object_ty;
  26922 
  26923     switch (inner_ty.zigTypeTag(zcu)) {
  26924         .array => {
  26925             if (field_name.eqlSlice("len", ip)) {
  26926                 const int_val = try pt.intValue(.usize, inner_ty.arrayLen(zcu));
  26927                 return uavRef(sema, int_val.toIntern());
  26928             } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
  26929                 const ptr_info = object_ty.ptrInfo(zcu);
  26930                 const new_ptr_ty = try pt.ptrTypeSema(.{
  26931                     .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(),
  26932                     .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  26933                     .flags = .{
  26934                         .size = .many,
  26935                         .alignment = ptr_info.flags.alignment,
  26936                         .is_const = ptr_info.flags.is_const,
  26937                         .is_volatile = ptr_info.flags.is_volatile,
  26938                         .is_allowzero = ptr_info.flags.is_allowzero,
  26939                         .address_space = ptr_info.flags.address_space,
  26940                         .vector_index = ptr_info.flags.vector_index,
  26941                     },
  26942                     .packed_offset = ptr_info.packed_offset,
  26943                 });
  26944                 const ptr_ptr_info = object_ptr_ty.ptrInfo(zcu);
  26945                 const result_ty = try pt.ptrTypeSema(.{
  26946                     .child = new_ptr_ty.toIntern(),
  26947                     .sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  26948                     .flags = .{
  26949                         .alignment = ptr_ptr_info.flags.alignment,
  26950                         .is_const = ptr_ptr_info.flags.is_const,
  26951                         .is_volatile = ptr_ptr_info.flags.is_volatile,
  26952                         .is_allowzero = ptr_ptr_info.flags.is_allowzero,
  26953                         .address_space = ptr_ptr_info.flags.address_space,
  26954                         .vector_index = ptr_ptr_info.flags.vector_index,
  26955                     },
  26956                     .packed_offset = ptr_ptr_info.packed_offset,
  26957                 });
  26958                 return sema.bitCast(block, result_ty, object_ptr, src, null);
  26959             } else {
  26960                 return sema.fail(
  26961                     block,
  26962                     field_name_src,
  26963                     "no member named '{f}' in '{f}'",
  26964                     .{ field_name.fmt(ip), object_ty.fmt(pt) },
  26965                 );
  26966             }
  26967         },
  26968         .pointer => if (inner_ty.isSlice(zcu)) {
  26969             const inner_ptr = if (is_pointer_to)
  26970                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  26971             else
  26972                 object_ptr;
  26973 
  26974             const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty;
  26975 
  26976             if (field_name.eqlSlice("ptr", ip)) {
  26977                 const slice_ptr_ty = inner_ty.slicePtrFieldType(zcu);
  26978 
  26979                 const result_ty = try pt.ptrTypeSema(.{
  26980                     .child = slice_ptr_ty.toIntern(),
  26981                     .flags = .{
  26982                         .is_const = !attr_ptr_ty.ptrIsMutable(zcu),
  26983                         .is_volatile = attr_ptr_ty.isVolatilePtr(zcu),
  26984                         .address_space = attr_ptr_ty.ptrAddressSpace(zcu),
  26985                     },
  26986                 });
  26987 
  26988                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  26989                     return Air.internedToRef((try val.ptrField(Value.slice_ptr_index, pt)).toIntern());
  26990                 }
  26991                 try sema.requireRuntimeBlock(block, src, null);
  26992 
  26993                 const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
  26994                 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
  26995                 return field_ptr;
  26996             } else if (field_name.eqlSlice("len", ip)) {
  26997                 const result_ty = try pt.ptrTypeSema(.{
  26998                     .child = .usize_type,
  26999                     .flags = .{
  27000                         .is_const = !attr_ptr_ty.ptrIsMutable(zcu),
  27001                         .is_volatile = attr_ptr_ty.isVolatilePtr(zcu),
  27002                         .address_space = attr_ptr_ty.ptrAddressSpace(zcu),
  27003                     },
  27004                 });
  27005 
  27006                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  27007                     return Air.internedToRef((try val.ptrField(Value.slice_len_index, pt)).toIntern());
  27008                 }
  27009                 try sema.requireRuntimeBlock(block, src, null);
  27010 
  27011                 const field_ptr = try block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr);
  27012                 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
  27013                 return field_ptr;
  27014             } else {
  27015                 return sema.fail(
  27016                     block,
  27017                     field_name_src,
  27018                     "no member named '{f}' in '{f}'",
  27019                     .{ field_name.fmt(ip), object_ty.fmt(pt) },
  27020                 );
  27021             }
  27022         },
  27023         .type => {
  27024             _ = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, object_ptr, undefined);
  27025             const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
  27026             const inner = if (is_pointer_to)
  27027                 try sema.analyzeLoad(block, src, result, object_ptr_src)
  27028             else
  27029                 result;
  27030 
  27031             const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
  27032             const child_type = val.toType();
  27033 
  27034             switch (child_type.zigTypeTag(zcu)) {
  27035                 .error_set => {
  27036                     switch (ip.indexToKey(child_type.toIntern())) {
  27037                         .error_set_type => |error_set_type| blk: {
  27038                             if (error_set_type.nameIndex(ip, field_name) != null) {
  27039                                 break :blk;
  27040                             }
  27041                             return sema.fail(block, src, "no error named '{f}' in '{f}'", .{
  27042                                 field_name.fmt(ip), child_type.fmt(pt),
  27043                             });
  27044                         },
  27045                         .inferred_error_set_type => {
  27046                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  27047                         },
  27048                         .simple_type => |t| {
  27049                             assert(t == .anyerror);
  27050                             _ = try pt.getErrorValue(field_name);
  27051                         },
  27052                         else => unreachable,
  27053                     }
  27054 
  27055                     const error_set_type = if (!child_type.isAnyError(zcu))
  27056                         child_type
  27057                     else
  27058                         try pt.singleErrorSetType(field_name);
  27059                     return uavRef(sema, try pt.intern(.{ .err = .{
  27060                         .ty = error_set_type.toIntern(),
  27061                         .name = field_name,
  27062                     } }));
  27063                 },
  27064                 .@"union" => {
  27065                     if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  27066                         return inst;
  27067                     }
  27068                     try child_type.resolveFields(pt);
  27069                     if (child_type.unionTagType(zcu)) |enum_ty| {
  27070                         if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| {
  27071                             const field_index_u32: u32 = @intCast(field_index);
  27072                             const idx_val = try pt.enumValueFieldIndex(enum_ty, field_index_u32);
  27073                             return uavRef(sema, idx_val.toIntern());
  27074                         }
  27075                     }
  27076                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  27077                 },
  27078                 .@"enum" => {
  27079                     if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  27080                         return inst;
  27081                     }
  27082                     const field_index = child_type.enumFieldIndex(field_name, zcu) orelse {
  27083                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  27084                     };
  27085                     const field_index_u32: u32 = @intCast(field_index);
  27086                     const idx_val = try pt.enumValueFieldIndex(child_type, field_index_u32);
  27087                     return uavRef(sema, idx_val.toIntern());
  27088                 },
  27089                 .@"struct", .@"opaque" => {
  27090                     if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  27091                         return inst;
  27092                     }
  27093                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  27094                 },
  27095                 else => return sema.fail(block, src, "type '{f}' has no members", .{child_type.fmt(pt)}),
  27096             }
  27097         },
  27098         .@"struct" => {
  27099             const inner_ptr = if (is_pointer_to)
  27100                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  27101             else
  27102                 object_ptr;
  27103             const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  27104             try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
  27105             return field_ptr;
  27106         },
  27107         .@"union" => {
  27108             const inner_ptr = if (is_pointer_to)
  27109                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  27110             else
  27111                 object_ptr;
  27112             const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  27113             try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
  27114             return field_ptr;
  27115         },
  27116         else => {},
  27117     }
  27118     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  27119 }
  27120 
  27121 const ResolvedFieldCallee = union(enum) {
  27122     /// The LHS of the call was an actual field with this value.
  27123     direct: Air.Inst.Ref,
  27124     /// This is a method call, with the function and first argument given.
  27125     method: struct {
  27126         func_inst: Air.Inst.Ref,
  27127         arg0_inst: Air.Inst.Ref,
  27128     },
  27129 };
  27130 
  27131 fn fieldCallBind(
  27132     sema: *Sema,
  27133     block: *Block,
  27134     src: LazySrcLoc,
  27135     raw_ptr: Air.Inst.Ref,
  27136     field_name: InternPool.NullTerminatedString,
  27137     field_name_src: LazySrcLoc,
  27138 ) CompileError!ResolvedFieldCallee {
  27139     // When editing this function, note that there is corresponding logic to be edited
  27140     // in `fieldVal`. This function takes a pointer and returns a pointer.
  27141 
  27142     const pt = sema.pt;
  27143     const zcu = pt.zcu;
  27144     const ip = &zcu.intern_pool;
  27145     const raw_ptr_src = src; // TODO better source location
  27146     const raw_ptr_ty = sema.typeOf(raw_ptr);
  27147     const inner_ty = if (raw_ptr_ty.zigTypeTag(zcu) == .pointer and (raw_ptr_ty.ptrSize(zcu) == .one or raw_ptr_ty.ptrSize(zcu) == .c))
  27148         raw_ptr_ty.childType(zcu)
  27149     else
  27150         return sema.fail(block, raw_ptr_src, "expected single pointer, found '{f}'", .{raw_ptr_ty.fmt(pt)});
  27151 
  27152     // Optionally dereference a second pointer to get the concrete type.
  27153     const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one;
  27154     const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty;
  27155     const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty;
  27156     const object_ptr = if (is_double_ptr)
  27157         try sema.analyzeLoad(block, src, raw_ptr, src)
  27158     else
  27159         raw_ptr;
  27160 
  27161     find_field: {
  27162         switch (concrete_ty.zigTypeTag(zcu)) {
  27163             .@"struct" => {
  27164                 try concrete_ty.resolveFields(pt);
  27165                 if (zcu.typeToStruct(concrete_ty)) |struct_type| {
  27166                     const field_index = struct_type.nameIndex(ip, field_name) orelse
  27167                         break :find_field;
  27168                     const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
  27169 
  27170                     return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr);
  27171                 } else if (concrete_ty.isTuple(zcu)) {
  27172                     if (field_name.eqlSlice("len", ip)) {
  27173                         return .{ .direct = try pt.intRef(.usize, concrete_ty.structFieldCount(zcu)) };
  27174                     }
  27175                     if (field_name.toUnsigned(ip)) |field_index| {
  27176                         if (field_index >= concrete_ty.structFieldCount(zcu)) break :find_field;
  27177                         return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.fieldType(field_index, zcu), field_index, object_ptr);
  27178                     }
  27179                 } else {
  27180                     const max = concrete_ty.structFieldCount(zcu);
  27181                     for (0..max) |i_usize| {
  27182                         const i: u32 = @intCast(i_usize);
  27183                         if (field_name == concrete_ty.structFieldName(i, zcu).unwrap().?) {
  27184                             return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.fieldType(i, zcu), i, object_ptr);
  27185                         }
  27186                     }
  27187                 }
  27188             },
  27189             .@"union" => {
  27190                 try concrete_ty.resolveFields(pt);
  27191                 const union_obj = zcu.typeToUnion(concrete_ty).?;
  27192                 _ = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse break :find_field;
  27193                 const field_ptr = try unionFieldPtr(sema, block, src, object_ptr, field_name, field_name_src, concrete_ty, false);
  27194                 return .{ .direct = try sema.analyzeLoad(block, src, field_ptr, src) };
  27195             },
  27196             .type => {
  27197                 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
  27198                 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) };
  27199             },
  27200             else => {},
  27201         }
  27202     }
  27203 
  27204     // If we get here, we need to look for a decl in the struct type instead.
  27205     const found_nav = found_nav: {
  27206         const namespace = concrete_ty.getNamespace(zcu).unwrap() orelse
  27207             break :found_nav null;
  27208         const nav_index = try sema.namespaceLookup(block, src, namespace, field_name) orelse
  27209             break :found_nav null;
  27210 
  27211         const decl_val = try sema.analyzeNavVal(block, src, nav_index);
  27212         const decl_type = sema.typeOf(decl_val);
  27213         if (zcu.typeToFunc(decl_type)) |func_type| f: {
  27214             if (func_type.param_types.len == 0) break :f;
  27215 
  27216             const first_param_type: Type = .fromInterned(func_type.param_types.get(ip)[0]);
  27217             if (first_param_type.isGenericPoison() or
  27218                 (first_param_type.zigTypeTag(zcu) == .pointer and
  27219                     (first_param_type.ptrSize(zcu) == .one or
  27220                         first_param_type.ptrSize(zcu) == .c) and
  27221                     first_param_type.childType(zcu).eql(concrete_ty, zcu)))
  27222             {
  27223                 // Note that if the param type is generic poison, we know that it must
  27224                 // specifically be `anytype` since it's the first parameter, meaning we
  27225                 // can safely assume it can be a pointer.
  27226                 // TODO: bound fn calls on rvalues should probably
  27227                 // generate a by-value argument somehow.
  27228                 return .{ .method = .{
  27229                     .func_inst = decl_val,
  27230                     .arg0_inst = object_ptr,
  27231                 } };
  27232             } else if (first_param_type.eql(concrete_ty, zcu)) {
  27233                 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  27234                 return .{ .method = .{
  27235                     .func_inst = decl_val,
  27236                     .arg0_inst = deref,
  27237                 } };
  27238             } else if (first_param_type.zigTypeTag(zcu) == .optional) {
  27239                 const child = first_param_type.optionalChild(zcu);
  27240                 if (child.eql(concrete_ty, zcu)) {
  27241                     const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  27242                     return .{ .method = .{
  27243                         .func_inst = decl_val,
  27244                         .arg0_inst = deref,
  27245                     } };
  27246                 } else if (child.zigTypeTag(zcu) == .pointer and
  27247                     child.ptrSize(zcu) == .one and
  27248                     child.childType(zcu).eql(concrete_ty, zcu))
  27249                 {
  27250                     return .{ .method = .{
  27251                         .func_inst = decl_val,
  27252                         .arg0_inst = object_ptr,
  27253                     } };
  27254                 }
  27255             } else if (first_param_type.zigTypeTag(zcu) == .error_union and
  27256                 first_param_type.errorUnionPayload(zcu).eql(concrete_ty, zcu))
  27257             {
  27258                 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  27259                 return .{ .method = .{
  27260                     .func_inst = decl_val,
  27261                     .arg0_inst = deref,
  27262                 } };
  27263             }
  27264         }
  27265         break :found_nav nav_index;
  27266     };
  27267 
  27268     const msg = msg: {
  27269         const msg = try sema.errMsg(src, "no field or member function named '{f}' in '{f}'", .{
  27270             field_name.fmt(ip),
  27271             concrete_ty.fmt(pt),
  27272         });
  27273         errdefer msg.destroy(sema.gpa);
  27274         try sema.addDeclaredHereNote(msg, concrete_ty);
  27275         if (found_nav) |nav_index| {
  27276             try sema.errNote(
  27277                 zcu.navSrcLoc(nav_index),
  27278                 msg,
  27279                 "'{f}' is not a member function",
  27280                 .{field_name.fmt(ip)},
  27281             );
  27282         }
  27283         if (concrete_ty.zigTypeTag(zcu) == .error_union) {
  27284             try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
  27285         }
  27286         if (is_double_ptr) {
  27287             try sema.errNote(src, msg, "method invocation only supports up to one level of implicit pointer dereferencing", .{});
  27288             try sema.errNote(src, msg, "use '.*' to dereference pointer", .{});
  27289         }
  27290         break :msg msg;
  27291     };
  27292     return sema.failWithOwnedErrorMsg(block, msg);
  27293 }
  27294 
  27295 fn finishFieldCallBind(
  27296     sema: *Sema,
  27297     block: *Block,
  27298     src: LazySrcLoc,
  27299     ptr_ty: Type,
  27300     field_ty: Type,
  27301     field_index: u32,
  27302     object_ptr: Air.Inst.Ref,
  27303 ) CompileError!ResolvedFieldCallee {
  27304     const pt = sema.pt;
  27305     const zcu = pt.zcu;
  27306     const ptr_field_ty = try pt.ptrTypeSema(.{
  27307         .child = field_ty.toIntern(),
  27308         .flags = .{
  27309             .is_const = !ptr_ty.ptrIsMutable(zcu),
  27310             .address_space = ptr_ty.ptrAddressSpace(zcu),
  27311         },
  27312     });
  27313 
  27314     const container_ty = ptr_ty.childType(zcu);
  27315     if (container_ty.zigTypeTag(zcu) == .@"struct") {
  27316         if (container_ty.structFieldIsComptime(field_index, zcu)) {
  27317             try container_ty.resolveStructFieldInits(pt);
  27318             const default_val = (try container_ty.structFieldValueComptime(pt, field_index)).?;
  27319             return .{ .direct = Air.internedToRef(default_val.toIntern()) };
  27320         }
  27321     }
  27322 
  27323     if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
  27324         const ptr_val = try struct_ptr_val.ptrField(field_index, pt);
  27325         const pointer = Air.internedToRef(ptr_val.toIntern());
  27326         return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) };
  27327     }
  27328 
  27329     try sema.requireRuntimeBlock(block, src, null);
  27330     const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
  27331     return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) };
  27332 }
  27333 
  27334 fn namespaceLookup(
  27335     sema: *Sema,
  27336     block: *Block,
  27337     src: LazySrcLoc,
  27338     namespace: InternPool.NamespaceIndex,
  27339     decl_name: InternPool.NullTerminatedString,
  27340 ) CompileError!?InternPool.Nav.Index {
  27341     const pt = sema.pt;
  27342     const zcu = pt.zcu;
  27343     const gpa = sema.gpa;
  27344     if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| {
  27345         if (!lookup.accessible) {
  27346             return sema.failWithOwnedErrorMsg(block, msg: {
  27347                 const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{
  27348                     decl_name.fmt(&zcu.intern_pool),
  27349                 });
  27350                 errdefer msg.destroy(gpa);
  27351                 try sema.errNote(zcu.navSrcLoc(lookup.nav), msg, "declared here", .{});
  27352                 break :msg msg;
  27353             });
  27354         }
  27355         return lookup.nav;
  27356     }
  27357     return null;
  27358 }
  27359 
  27360 fn namespaceLookupRef(
  27361     sema: *Sema,
  27362     block: *Block,
  27363     src: LazySrcLoc,
  27364     namespace: InternPool.NamespaceIndex,
  27365     decl_name: InternPool.NullTerminatedString,
  27366 ) CompileError!?Air.Inst.Ref {
  27367     const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null;
  27368     return try sema.analyzeNavRef(block, src, nav);
  27369 }
  27370 
  27371 fn namespaceLookupVal(
  27372     sema: *Sema,
  27373     block: *Block,
  27374     src: LazySrcLoc,
  27375     namespace: InternPool.NamespaceIndex,
  27376     decl_name: InternPool.NullTerminatedString,
  27377 ) CompileError!?Air.Inst.Ref {
  27378     const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null;
  27379     return try sema.analyzeNavVal(block, src, nav);
  27380 }
  27381 
  27382 fn structFieldPtr(
  27383     sema: *Sema,
  27384     block: *Block,
  27385     src: LazySrcLoc,
  27386     struct_ptr: Air.Inst.Ref,
  27387     field_name: InternPool.NullTerminatedString,
  27388     field_name_src: LazySrcLoc,
  27389     struct_ty: Type,
  27390     initializing: bool,
  27391 ) CompileError!Air.Inst.Ref {
  27392     const pt = sema.pt;
  27393     const zcu = pt.zcu;
  27394     const ip = &zcu.intern_pool;
  27395     assert(struct_ty.zigTypeTag(zcu) == .@"struct");
  27396 
  27397     try struct_ty.resolveFields(pt);
  27398     try struct_ty.resolveLayout(pt);
  27399 
  27400     if (struct_ty.isTuple(zcu)) {
  27401         if (field_name.eqlSlice("len", ip)) {
  27402             const len_inst = try pt.intRef(.usize, struct_ty.structFieldCount(zcu));
  27403             return sema.analyzeRef(block, src, len_inst);
  27404         }
  27405         const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
  27406         return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
  27407     }
  27408 
  27409     const struct_type = zcu.typeToStruct(struct_ty).?;
  27410 
  27411     const field_index = struct_type.nameIndex(ip, field_name) orelse
  27412         return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
  27413 
  27414     return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty);
  27415 }
  27416 
  27417 fn structFieldPtrByIndex(
  27418     sema: *Sema,
  27419     block: *Block,
  27420     src: LazySrcLoc,
  27421     struct_ptr: Air.Inst.Ref,
  27422     field_index: u32,
  27423     struct_ty: Type,
  27424 ) CompileError!Air.Inst.Ref {
  27425     const pt = sema.pt;
  27426     const zcu = pt.zcu;
  27427     const ip = &zcu.intern_pool;
  27428 
  27429     const struct_type = zcu.typeToStruct(struct_ty).?;
  27430     const field_is_comptime = struct_type.fieldIsComptime(ip, field_index);
  27431 
  27432     // Comptime fields are handled later
  27433     if (!field_is_comptime) {
  27434         if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
  27435             const val = try struct_ptr_val.ptrField(field_index, pt);
  27436             return Air.internedToRef(val.toIntern());
  27437         }
  27438     }
  27439 
  27440     const field_ty = struct_type.field_types.get(ip)[field_index];
  27441     const struct_ptr_ty = sema.typeOf(struct_ptr);
  27442     const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);
  27443 
  27444     var ptr_ty_data: InternPool.Key.PtrType = .{
  27445         .child = field_ty,
  27446         .flags = .{
  27447             .is_const = struct_ptr_ty_info.flags.is_const,
  27448             .is_volatile = struct_ptr_ty_info.flags.is_volatile,
  27449             .address_space = struct_ptr_ty_info.flags.address_space,
  27450         },
  27451     };
  27452 
  27453     const parent_align = if (struct_ptr_ty_info.flags.alignment != .none)
  27454         struct_ptr_ty_info.flags.alignment
  27455     else
  27456         try Type.fromInterned(struct_ptr_ty_info.child).abiAlignmentSema(pt);
  27457 
  27458     if (struct_type.layout == .@"packed") {
  27459         assert(!field_is_comptime);
  27460         switch (struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt)) {
  27461             .bit_ptr => |packed_offset| {
  27462                 ptr_ty_data.flags.alignment = parent_align;
  27463                 ptr_ty_data.packed_offset = packed_offset;
  27464             },
  27465             .byte_ptr => |ptr_info| {
  27466                 ptr_ty_data.flags.alignment = ptr_info.alignment;
  27467             },
  27468         }
  27469     } else if (struct_type.layout == .@"extern") {
  27470         assert(!field_is_comptime);
  27471         // For extern structs, field alignment might be bigger than type's
  27472         // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the
  27473         // second field is aligned as u32.
  27474         const field_offset = struct_ty.structFieldOffset(field_index, zcu);
  27475         ptr_ty_data.flags.alignment = if (parent_align == .none)
  27476             .none
  27477         else
  27478             @enumFromInt(@min(@intFromEnum(parent_align), @ctz(field_offset)));
  27479     } else {
  27480         // Our alignment is capped at the field alignment.
  27481         const field_align = try Type.fromInterned(field_ty).structFieldAlignmentSema(
  27482             struct_type.fieldAlign(ip, field_index),
  27483             struct_type.layout,
  27484             pt,
  27485         );
  27486         ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none)
  27487             field_align
  27488         else
  27489             field_align.min(parent_align);
  27490     }
  27491 
  27492     const ptr_field_ty = try pt.ptrTypeSema(ptr_ty_data);
  27493 
  27494     if (field_is_comptime) {
  27495         try struct_ty.resolveStructFieldInits(pt);
  27496         const val = try pt.intern(.{ .ptr = .{
  27497             .ty = ptr_field_ty.toIntern(),
  27498             .base_addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] },
  27499             .byte_offset = 0,
  27500         } });
  27501         return Air.internedToRef(val);
  27502     }
  27503 
  27504     return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
  27505 }
  27506 
  27507 fn structFieldVal(
  27508     sema: *Sema,
  27509     block: *Block,
  27510     struct_byval: Air.Inst.Ref,
  27511     field_name: InternPool.NullTerminatedString,
  27512     field_name_src: LazySrcLoc,
  27513     struct_ty: Type,
  27514 ) CompileError!Air.Inst.Ref {
  27515     const pt = sema.pt;
  27516     const zcu = pt.zcu;
  27517     const ip = &zcu.intern_pool;
  27518     assert(struct_ty.zigTypeTag(zcu) == .@"struct");
  27519 
  27520     try struct_ty.resolveFields(pt);
  27521 
  27522     switch (ip.indexToKey(struct_ty.toIntern())) {
  27523         .struct_type => {
  27524             const struct_type = ip.loadStructType(struct_ty.toIntern());
  27525 
  27526             const field_index = struct_type.nameIndex(ip, field_name) orelse
  27527                 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
  27528             if (struct_type.fieldIsComptime(ip, field_index)) {
  27529                 try struct_ty.resolveStructFieldInits(pt);
  27530                 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]);
  27531             }
  27532 
  27533             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
  27534             if (try sema.typeHasOnePossibleValue(field_ty)) |field_val|
  27535                 return Air.internedToRef(field_val.toIntern());
  27536 
  27537             if (try sema.resolveValue(struct_byval)) |struct_val| {
  27538                 if (struct_val.isUndef(zcu)) return pt.undefRef(field_ty);
  27539                 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
  27540                     return Air.internedToRef(opv.toIntern());
  27541                 }
  27542                 return Air.internedToRef((try struct_val.fieldValue(pt, field_index)).toIntern());
  27543             }
  27544 
  27545             try field_ty.resolveLayout(pt);
  27546             return block.addStructFieldVal(struct_byval, field_index, field_ty);
  27547         },
  27548         .tuple_type => {
  27549             return sema.tupleFieldVal(block, struct_byval, field_name, field_name_src, struct_ty);
  27550         },
  27551         else => unreachable,
  27552     }
  27553 }
  27554 
  27555 fn tupleFieldVal(
  27556     sema: *Sema,
  27557     block: *Block,
  27558     tuple_byval: Air.Inst.Ref,
  27559     field_name: InternPool.NullTerminatedString,
  27560     field_name_src: LazySrcLoc,
  27561     tuple_ty: Type,
  27562 ) CompileError!Air.Inst.Ref {
  27563     const pt = sema.pt;
  27564     const zcu = pt.zcu;
  27565     if (field_name.eqlSlice("len", &zcu.intern_pool)) {
  27566         return pt.intRef(.usize, tuple_ty.structFieldCount(zcu));
  27567     }
  27568     const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src);
  27569     return sema.tupleFieldValByIndex(block, tuple_byval, field_index, tuple_ty);
  27570 }
  27571 
  27572 /// Asserts that `field_name` is not "len".
  27573 fn tupleFieldIndex(
  27574     sema: *Sema,
  27575     block: *Block,
  27576     tuple_ty: Type,
  27577     field_name: InternPool.NullTerminatedString,
  27578     field_name_src: LazySrcLoc,
  27579 ) CompileError!u32 {
  27580     const pt = sema.pt;
  27581     const ip = &pt.zcu.intern_pool;
  27582     assert(!field_name.eqlSlice("len", ip));
  27583     if (field_name.toUnsigned(ip)) |field_index| {
  27584         if (field_index < tuple_ty.structFieldCount(pt.zcu)) return field_index;
  27585         return sema.fail(block, field_name_src, "index '{f}' out of bounds of tuple '{f}'", .{
  27586             field_name.fmt(ip), tuple_ty.fmt(pt),
  27587         });
  27588     }
  27589 
  27590     return sema.fail(block, field_name_src, "no field named '{f}' in tuple '{f}'", .{
  27591         field_name.fmt(ip), tuple_ty.fmt(pt),
  27592     });
  27593 }
  27594 
  27595 fn tupleFieldValByIndex(
  27596     sema: *Sema,
  27597     block: *Block,
  27598     tuple_byval: Air.Inst.Ref,
  27599     field_index: u32,
  27600     tuple_ty: Type,
  27601 ) CompileError!Air.Inst.Ref {
  27602     const pt = sema.pt;
  27603     const zcu = pt.zcu;
  27604     const field_ty = tuple_ty.fieldType(field_index, zcu);
  27605 
  27606     if (tuple_ty.structFieldIsComptime(field_index, zcu))
  27607         try tuple_ty.resolveStructFieldInits(pt);
  27608     if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| {
  27609         return Air.internedToRef(default_value.toIntern());
  27610     }
  27611 
  27612     if (try sema.resolveValue(tuple_byval)) |tuple_val| {
  27613         if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
  27614             return Air.internedToRef(opv.toIntern());
  27615         }
  27616         return switch (zcu.intern_pool.indexToKey(tuple_val.toIntern())) {
  27617             .undef => pt.undefRef(field_ty),
  27618             .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) {
  27619                 .bytes => |bytes| try pt.intValue(.u8, bytes.at(field_index, &zcu.intern_pool)),
  27620                 .elems => |elems| Value.fromInterned(elems[field_index]),
  27621                 .repeated_elem => |elem| Value.fromInterned(elem),
  27622             }.toIntern()),
  27623             else => unreachable,
  27624         };
  27625     }
  27626 
  27627     try field_ty.resolveLayout(pt);
  27628     return block.addStructFieldVal(tuple_byval, field_index, field_ty);
  27629 }
  27630 
  27631 fn unionFieldPtr(
  27632     sema: *Sema,
  27633     block: *Block,
  27634     src: LazySrcLoc,
  27635     union_ptr: Air.Inst.Ref,
  27636     field_name: InternPool.NullTerminatedString,
  27637     field_name_src: LazySrcLoc,
  27638     union_ty: Type,
  27639     initializing: bool,
  27640 ) CompileError!Air.Inst.Ref {
  27641     const pt = sema.pt;
  27642     const zcu = pt.zcu;
  27643     const ip = &zcu.intern_pool;
  27644 
  27645     assert(union_ty.zigTypeTag(zcu) == .@"union");
  27646 
  27647     const union_ptr_ty = sema.typeOf(union_ptr);
  27648     const union_ptr_info = union_ptr_ty.ptrInfo(zcu);
  27649     try union_ty.resolveFields(pt);
  27650     const union_obj = zcu.typeToUnion(union_ty).?;
  27651     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  27652     const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  27653     const ptr_field_ty = try pt.ptrTypeSema(.{
  27654         .child = field_ty.toIntern(),
  27655         .flags = .{
  27656             .is_const = union_ptr_info.flags.is_const,
  27657             .is_volatile = union_ptr_info.flags.is_volatile,
  27658             .address_space = union_ptr_info.flags.address_space,
  27659             .alignment = if (union_obj.flagsUnordered(ip).layout == .auto) blk: {
  27660                 const union_align = if (union_ptr_info.flags.alignment != .none)
  27661                     union_ptr_info.flags.alignment
  27662                 else
  27663                     try union_ty.abiAlignmentSema(pt);
  27664                 const field_align = try union_ty.fieldAlignmentSema(field_index, pt);
  27665                 break :blk union_align.min(field_align);
  27666             } else union_ptr_info.flags.alignment,
  27667         },
  27668         .packed_offset = union_ptr_info.packed_offset,
  27669     });
  27670     const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?);
  27671 
  27672     if (initializing and field_ty.zigTypeTag(zcu) == .noreturn) {
  27673         const msg = msg: {
  27674             const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{});
  27675             errdefer msg.destroy(sema.gpa);
  27676 
  27677             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
  27678                 field_name.fmt(ip),
  27679             });
  27680             try sema.addDeclaredHereNote(msg, union_ty);
  27681             break :msg msg;
  27682         };
  27683         return sema.failWithOwnedErrorMsg(block, msg);
  27684     }
  27685 
  27686     if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
  27687         switch (union_obj.flagsUnordered(ip).layout) {
  27688             .auto => if (initializing) {
  27689                 if (!sema.isComptimeMutablePtr(union_ptr_val)) {
  27690                     // The initialization is a runtime operation.
  27691                     break :ct;
  27692                 }
  27693                 // Store to the union to initialize the tag.
  27694                 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27695                 const payload_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  27696                 const new_union_val = try pt.unionValue(union_ty, field_tag, try pt.undefValue(payload_ty));
  27697                 try sema.storePtrVal(block, src, union_ptr_val, new_union_val, union_ty);
  27698             } else {
  27699                 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse
  27700                     break :ct;
  27701                 if (union_val.isUndef(zcu)) {
  27702                     return sema.failWithUseOfUndef(block, src, null);
  27703                 }
  27704                 const un = ip.indexToKey(union_val.toIntern()).un;
  27705                 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27706                 const tag_matches = un.tag == field_tag.toIntern();
  27707                 if (!tag_matches) {
  27708                     const msg = msg: {
  27709                         const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
  27710                         const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu);
  27711                         const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
  27712                             field_name.fmt(ip),
  27713                             active_field_name.fmt(ip),
  27714                         });
  27715                         errdefer msg.destroy(sema.gpa);
  27716                         try sema.addDeclaredHereNote(msg, union_ty);
  27717                         break :msg msg;
  27718                     };
  27719                     return sema.failWithOwnedErrorMsg(block, msg);
  27720                 }
  27721             },
  27722             .@"packed", .@"extern" => {},
  27723         }
  27724         const field_ptr_val = try union_ptr_val.ptrField(field_index, pt);
  27725         return Air.internedToRef(field_ptr_val.toIntern());
  27726     }
  27727 
  27728     // If the union has a tag, we must either set or or safety check it depending on `initializing`.
  27729     tag: {
  27730         if (union_ty.containerLayout(zcu) != .auto) break :tag;
  27731         const tag_ty: Type = .fromInterned(union_obj.enum_tag_ty);
  27732         if (try sema.typeHasOnePossibleValue(tag_ty) != null) break :tag;
  27733         // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but
  27734         // only emit a safety check if it's available at runtime (i.e. it's safety-tagged).
  27735         const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index);
  27736         if (initializing) {
  27737             const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag));
  27738             try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store
  27739         } else if (block.wantSafety() and union_obj.hasTag(ip)) {
  27740             // The tag exists at runtime (safety tag), so emit a safety check.
  27741             // TODO would it be better if get_union_tag supported pointers to unions?
  27742             const union_val = try block.addTyOp(.load, union_ty, union_ptr);
  27743             const active_tag = try block.addTyOp(.get_union_tag, tag_ty, union_val);
  27744             try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(want_tag));
  27745         }
  27746     }
  27747     if (field_ty.zigTypeTag(zcu) == .noreturn) {
  27748         _ = try block.addNoOp(.unreach);
  27749         return .unreachable_value;
  27750     }
  27751     return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
  27752 }
  27753 
  27754 fn unionFieldVal(
  27755     sema: *Sema,
  27756     block: *Block,
  27757     src: LazySrcLoc,
  27758     union_byval: Air.Inst.Ref,
  27759     field_name: InternPool.NullTerminatedString,
  27760     field_name_src: LazySrcLoc,
  27761     union_ty: Type,
  27762 ) CompileError!Air.Inst.Ref {
  27763     const pt = sema.pt;
  27764     const zcu = pt.zcu;
  27765     const ip = &zcu.intern_pool;
  27766     assert(union_ty.zigTypeTag(zcu) == .@"union");
  27767 
  27768     try union_ty.resolveFields(pt);
  27769     const union_obj = zcu.typeToUnion(union_ty).?;
  27770     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  27771     const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  27772     const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?);
  27773 
  27774     if (try sema.resolveValue(union_byval)) |union_val| {
  27775         if (union_val.isUndef(zcu)) return pt.undefRef(field_ty);
  27776 
  27777         const un = ip.indexToKey(union_val.toIntern()).un;
  27778         const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27779         const tag_matches = un.tag == field_tag.toIntern();
  27780         switch (union_obj.flagsUnordered(ip).layout) {
  27781             .auto => {
  27782                 if (tag_matches) {
  27783                     return Air.internedToRef(un.val);
  27784                 } else {
  27785                     const msg = msg: {
  27786                         const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
  27787                         const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu);
  27788                         const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
  27789                             field_name.fmt(ip), active_field_name.fmt(ip),
  27790                         });
  27791                         errdefer msg.destroy(sema.gpa);
  27792                         try sema.addDeclaredHereNote(msg, union_ty);
  27793                         break :msg msg;
  27794                     };
  27795                     return sema.failWithOwnedErrorMsg(block, msg);
  27796                 }
  27797             },
  27798             .@"extern" => if (tag_matches) {
  27799                 // Fast path - no need to use bitcast logic.
  27800                 return Air.internedToRef(un.val);
  27801             } else if (try sema.bitCastVal(union_val, field_ty, 0, 0, 0)) |field_val| {
  27802                 return Air.internedToRef(field_val.toIntern());
  27803             },
  27804             .@"packed" => if (tag_matches) {
  27805                 // Fast path - no need to use bitcast logic.
  27806                 return Air.internedToRef(un.val);
  27807             } else if (try sema.bitCastVal(union_val, field_ty, 0, try union_ty.bitSizeSema(pt), 0)) |field_val| {
  27808                 return Air.internedToRef(field_val.toIntern());
  27809             },
  27810         }
  27811     }
  27812 
  27813     if (union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and
  27814         union_ty.unionTagTypeSafety(zcu) != null and union_obj.field_types.len > 1)
  27815     {
  27816         const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27817         const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern());
  27818         const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_ty), union_byval);
  27819         try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, wanted_tag);
  27820     }
  27821 
  27822     if (field_ty.zigTypeTag(zcu) == .noreturn) {
  27823         _ = try block.addNoOp(.unreach);
  27824         return .unreachable_value;
  27825     }
  27826 
  27827     if (try sema.typeHasOnePossibleValue(field_ty)) |field_only_value| {
  27828         return Air.internedToRef(field_only_value.toIntern());
  27829     }
  27830 
  27831     try field_ty.resolveLayout(pt);
  27832     return block.addStructFieldVal(union_byval, field_index, field_ty);
  27833 }
  27834 
  27835 fn elemPtr(
  27836     sema: *Sema,
  27837     block: *Block,
  27838     src: LazySrcLoc,
  27839     indexable_ptr: Air.Inst.Ref,
  27840     elem_index: Air.Inst.Ref,
  27841     elem_index_src: LazySrcLoc,
  27842     init: bool,
  27843     oob_safety: bool,
  27844 ) CompileError!Air.Inst.Ref {
  27845     const pt = sema.pt;
  27846     const zcu = pt.zcu;
  27847     const indexable_ptr_src = src; // TODO better source location
  27848     const indexable_ptr_ty = sema.typeOf(indexable_ptr);
  27849 
  27850     const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(zcu)) {
  27851         .pointer => indexable_ptr_ty.childType(zcu),
  27852         else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}),
  27853     };
  27854     try sema.checkIndexable(block, src, indexable_ty);
  27855 
  27856     const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) {
  27857         .array, .vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
  27858         .@"struct" => blk: {
  27859             // Tuple field access.
  27860             const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
  27861             const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt));
  27862             break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
  27863         },
  27864         else => {
  27865             const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
  27866             return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety);
  27867         },
  27868     };
  27869 
  27870     try sema.checkKnownAllocPtr(block, indexable_ptr, elem_ptr);
  27871     return elem_ptr;
  27872 }
  27873 
  27874 /// Asserts that the type of indexable is pointer.
  27875 fn elemPtrOneLayerOnly(
  27876     sema: *Sema,
  27877     block: *Block,
  27878     src: LazySrcLoc,
  27879     indexable: Air.Inst.Ref,
  27880     elem_index: Air.Inst.Ref,
  27881     elem_index_src: LazySrcLoc,
  27882     init: bool,
  27883     oob_safety: bool,
  27884 ) CompileError!Air.Inst.Ref {
  27885     const indexable_src = src; // TODO better source location
  27886     const indexable_ty = sema.typeOf(indexable);
  27887     const pt = sema.pt;
  27888     const zcu = pt.zcu;
  27889 
  27890     try sema.checkIndexable(block, src, indexable_ty);
  27891 
  27892     switch (indexable_ty.ptrSize(zcu)) {
  27893         .slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  27894         .many, .c => {
  27895             const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  27896             const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  27897             ct: {
  27898                 const ptr_val = maybe_ptr_val orelse break :ct;
  27899                 const index_val = maybe_index_val orelse break :ct;
  27900                 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  27901                 const elem_ptr = try ptr_val.ptrElem(index, pt);
  27902                 return Air.internedToRef(elem_ptr.toIntern());
  27903             }
  27904 
  27905             try sema.checkLogicalPtrOperation(block, src, indexable_ty);
  27906             const result_ty = try indexable_ty.elemPtrType(null, pt);
  27907 
  27908             try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_ty, indexable_src);
  27909             try sema.validateRuntimeValue(block, indexable_src, indexable);
  27910 
  27911             if (!try result_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) {
  27912                 // zero-bit child type; just bitcast the pointer
  27913                 return block.addBitCast(result_ty, indexable);
  27914             }
  27915 
  27916             return block.addPtrElemPtr(indexable, elem_index, result_ty);
  27917         },
  27918         .one => {
  27919             const child_ty = indexable_ty.childType(zcu);
  27920             const elem_ptr = switch (child_ty.zigTypeTag(zcu)) {
  27921                 .array, .vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety),
  27922                 .@"struct" => blk: {
  27923                     assert(child_ty.isTuple(zcu));
  27924                     const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
  27925                     const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt));
  27926                     break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
  27927                 },
  27928                 else => unreachable, // Guaranteed by checkIndexable
  27929             };
  27930             try sema.checkKnownAllocPtr(block, indexable, elem_ptr);
  27931             return elem_ptr;
  27932         },
  27933     }
  27934 }
  27935 
  27936 fn elemVal(
  27937     sema: *Sema,
  27938     block: *Block,
  27939     src: LazySrcLoc,
  27940     indexable: Air.Inst.Ref,
  27941     elem_index_uncasted: Air.Inst.Ref,
  27942     elem_index_src: LazySrcLoc,
  27943     oob_safety: bool,
  27944 ) CompileError!Air.Inst.Ref {
  27945     const indexable_src = src; // TODO better source location
  27946     const indexable_ty = sema.typeOf(indexable);
  27947     const pt = sema.pt;
  27948     const zcu = pt.zcu;
  27949 
  27950     try sema.checkIndexable(block, src, indexable_ty);
  27951 
  27952     // TODO in case of a vector of pointers, we need to detect whether the element
  27953     // index is a scalar or vector instead of unconditionally casting to usize.
  27954     const elem_index = try sema.coerce(block, .usize, elem_index_uncasted, elem_index_src);
  27955 
  27956     switch (indexable_ty.zigTypeTag(zcu)) {
  27957         .pointer => switch (indexable_ty.ptrSize(zcu)) {
  27958             .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  27959             .many, .c => {
  27960                 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  27961                 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  27962                 const elem_ty = indexable_ty.elemType2(zcu);
  27963 
  27964                 ct: {
  27965                     const indexable_val = maybe_indexable_val orelse break :ct;
  27966                     const index_val = maybe_index_val orelse break :ct;
  27967                     const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  27968                     const many_ptr_ty = try pt.manyConstPtrType(elem_ty);
  27969                     const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty);
  27970                     const elem_ptr_ty = try pt.singleConstPtrType(elem_ty);
  27971                     const elem_ptr_val = try many_ptr_val.ptrElem(index, pt);
  27972                     const elem_val = try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty) orelse break :ct;
  27973                     return Air.internedToRef((try pt.getCoerced(elem_val, elem_ty)).toIntern());
  27974                 }
  27975 
  27976                 if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| {
  27977                     return Air.internedToRef(elem_only_value.toIntern());
  27978                 }
  27979 
  27980                 try sema.checkLogicalPtrOperation(block, src, indexable_ty);
  27981                 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
  27982             },
  27983             .one => {
  27984                 arr_sent: {
  27985                     const inner_ty = indexable_ty.childType(zcu);
  27986                     if (inner_ty.zigTypeTag(zcu) != .array) break :arr_sent;
  27987                     const sentinel = inner_ty.sentinel(zcu) orelse break :arr_sent;
  27988                     const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
  27989                     const index = try sema.usizeCast(block, src, try index_val.toUnsignedIntSema(pt));
  27990                     if (index != inner_ty.arrayLen(zcu)) break :arr_sent;
  27991                     return Air.internedToRef(sentinel.toIntern());
  27992                 }
  27993                 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
  27994                 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
  27995             },
  27996         },
  27997         .array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  27998         .vector => {
  27999             // TODO: If the index is a vector, the result should be a vector.
  28000             return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety);
  28001         },
  28002         .@"struct" => {
  28003             // Tuple field access.
  28004             const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
  28005             const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt));
  28006             return sema.tupleField(block, indexable_src, indexable, elem_index_src, index);
  28007         },
  28008         else => unreachable,
  28009     }
  28010 }
  28011 
  28012 /// Called when the index or indexable is runtime known.
  28013 fn validateRuntimeElemAccess(
  28014     sema: *Sema,
  28015     block: *Block,
  28016     elem_index_src: LazySrcLoc,
  28017     elem_ty: Type,
  28018     parent_ty: Type,
  28019     parent_src: LazySrcLoc,
  28020 ) CompileError!void {
  28021     const pt = sema.pt;
  28022     const zcu = pt.zcu;
  28023 
  28024     if (try elem_ty.comptimeOnlySema(sema.pt)) {
  28025         const msg = msg: {
  28026             const msg = try sema.errMsg(
  28027                 elem_index_src,
  28028                 "values of type '{f}' must be comptime-known, but index value is runtime-known",
  28029                 .{parent_ty.fmt(sema.pt)},
  28030             );
  28031             errdefer msg.destroy(sema.gpa);
  28032 
  28033             try sema.explainWhyTypeIsComptime(msg, parent_src, parent_ty);
  28034 
  28035             break :msg msg;
  28036         };
  28037         return sema.failWithOwnedErrorMsg(block, msg);
  28038     }
  28039 
  28040     if (zcu.intern_pool.indexToKey(parent_ty.toIntern()) == .ptr_type) {
  28041         const target = zcu.getTarget();
  28042         const as = parent_ty.ptrAddressSpace(zcu);
  28043         if (target_util.arePointersLogical(target, as)) {
  28044             return sema.fail(block, elem_index_src, "cannot access element of logical pointer '{f}'", .{parent_ty.fmt(pt)});
  28045         }
  28046     }
  28047 }
  28048 
  28049 fn tupleFieldPtr(
  28050     sema: *Sema,
  28051     block: *Block,
  28052     tuple_ptr_src: LazySrcLoc,
  28053     tuple_ptr: Air.Inst.Ref,
  28054     field_index_src: LazySrcLoc,
  28055     field_index: u32,
  28056     init: bool,
  28057 ) CompileError!Air.Inst.Ref {
  28058     const pt = sema.pt;
  28059     const zcu = pt.zcu;
  28060     const tuple_ptr_ty = sema.typeOf(tuple_ptr);
  28061     const tuple_ptr_info = tuple_ptr_ty.ptrInfo(zcu);
  28062     const tuple_ty: Type = .fromInterned(tuple_ptr_info.child);
  28063     try tuple_ty.resolveFields(pt);
  28064     const field_count = tuple_ty.structFieldCount(zcu);
  28065 
  28066     if (field_count == 0) {
  28067         return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
  28068     }
  28069 
  28070     if (field_index >= field_count) {
  28071         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  28072             field_index, field_count,
  28073         });
  28074     }
  28075 
  28076     const field_ty = tuple_ty.fieldType(field_index, zcu);
  28077     const ptr_field_ty = try pt.ptrTypeSema(.{
  28078         .child = field_ty.toIntern(),
  28079         .flags = .{
  28080             .is_const = tuple_ptr_info.flags.is_const,
  28081             .is_volatile = tuple_ptr_info.flags.is_volatile,
  28082             .address_space = tuple_ptr_info.flags.address_space,
  28083             .alignment = a: {
  28084                 if (tuple_ptr_info.flags.alignment == .none) break :a .none;
  28085                 // The tuple pointer isn't naturally aligned, so the field pointer might be underaligned.
  28086                 const tuple_align = tuple_ptr_info.flags.alignment;
  28087                 const field_align = try field_ty.abiAlignmentSema(pt);
  28088                 break :a tuple_align.min(field_align);
  28089             },
  28090         },
  28091     });
  28092 
  28093     if (tuple_ty.structFieldIsComptime(field_index, zcu))
  28094         try tuple_ty.resolveStructFieldInits(pt);
  28095 
  28096     if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_val| {
  28097         return Air.internedToRef((try pt.intern(.{ .ptr = .{
  28098             .ty = ptr_field_ty.toIntern(),
  28099             .base_addr = .{ .comptime_field = default_val.toIntern() },
  28100             .byte_offset = 0,
  28101         } })));
  28102     }
  28103 
  28104     if (try sema.resolveValue(tuple_ptr)) |tuple_ptr_val| {
  28105         const field_ptr_val = try tuple_ptr_val.ptrField(field_index, pt);
  28106         return Air.internedToRef(field_ptr_val.toIntern());
  28107     }
  28108 
  28109     if (!init) {
  28110         try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src);
  28111     }
  28112 
  28113     return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
  28114 }
  28115 
  28116 fn tupleField(
  28117     sema: *Sema,
  28118     block: *Block,
  28119     tuple_src: LazySrcLoc,
  28120     tuple: Air.Inst.Ref,
  28121     field_index_src: LazySrcLoc,
  28122     field_index: u32,
  28123 ) CompileError!Air.Inst.Ref {
  28124     const pt = sema.pt;
  28125     const zcu = pt.zcu;
  28126     const tuple_ty = sema.typeOf(tuple);
  28127     try tuple_ty.resolveFields(pt);
  28128     const field_count = tuple_ty.structFieldCount(zcu);
  28129 
  28130     if (field_count == 0) {
  28131         return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});
  28132     }
  28133 
  28134     if (field_index >= field_count) {
  28135         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  28136             field_index, field_count,
  28137         });
  28138     }
  28139 
  28140     const field_ty = tuple_ty.fieldType(field_index, zcu);
  28141 
  28142     if (tuple_ty.structFieldIsComptime(field_index, zcu))
  28143         try tuple_ty.resolveStructFieldInits(pt);
  28144     if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| {
  28145         return Air.internedToRef(default_value.toIntern()); // comptime field
  28146     }
  28147 
  28148     if (try sema.resolveValue(tuple)) |tuple_val| {
  28149         if (tuple_val.isUndef(zcu)) return pt.undefRef(field_ty);
  28150         return Air.internedToRef((try tuple_val.fieldValue(pt, field_index)).toIntern());
  28151     }
  28152 
  28153     try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src);
  28154 
  28155     try field_ty.resolveLayout(pt);
  28156     return block.addStructFieldVal(tuple, field_index, field_ty);
  28157 }
  28158 
  28159 fn elemValArray(
  28160     sema: *Sema,
  28161     block: *Block,
  28162     src: LazySrcLoc,
  28163     array_src: LazySrcLoc,
  28164     array: Air.Inst.Ref,
  28165     elem_index_src: LazySrcLoc,
  28166     elem_index: Air.Inst.Ref,
  28167     oob_safety: bool,
  28168 ) CompileError!Air.Inst.Ref {
  28169     const pt = sema.pt;
  28170     const zcu = pt.zcu;
  28171     const array_ty = sema.typeOf(array);
  28172     const array_sent = array_ty.sentinel(zcu);
  28173     const array_len = array_ty.arrayLen(zcu);
  28174     const array_len_s = array_len + @intFromBool(array_sent != null);
  28175     const elem_ty = array_ty.childType(zcu);
  28176 
  28177     if (array_len_s == 0) {
  28178         return sema.fail(block, array_src, "indexing into empty array is not allowed", .{});
  28179     }
  28180 
  28181     const maybe_undef_array_val = try sema.resolveValue(array);
  28182     // index must be defined since it can access out of bounds
  28183     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  28184 
  28185     if (maybe_index_val) |index_val| {
  28186         const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  28187         if (array_sent) |s| {
  28188             if (index == array_len) {
  28189                 return Air.internedToRef(s.toIntern());
  28190             }
  28191         }
  28192         if (index >= array_len_s) {
  28193             const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else "";
  28194             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  28195         }
  28196     }
  28197     if (maybe_undef_array_val) |array_val| {
  28198         if (array_val.isUndef(zcu)) {
  28199             return pt.undefRef(elem_ty);
  28200         }
  28201         if (maybe_index_val) |index_val| {
  28202             const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  28203             const elem_val = try array_val.elemValue(pt, index);
  28204             return Air.internedToRef(elem_val.toIntern());
  28205         }
  28206     }
  28207 
  28208     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src);
  28209     try sema.validateRuntimeValue(block, array_src, array);
  28210 
  28211     if (oob_safety and block.wantSafety()) {
  28212         // Runtime check is only needed if unable to comptime check.
  28213         if (maybe_index_val == null) {
  28214             const len_inst = try pt.intRef(.usize, array_len);
  28215             const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
  28216             try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
  28217         }
  28218     }
  28219 
  28220     if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_val|
  28221         return Air.internedToRef(elem_val.toIntern());
  28222 
  28223     return block.addBinOp(.array_elem_val, array, elem_index);
  28224 }
  28225 
  28226 fn elemPtrArray(
  28227     sema: *Sema,
  28228     block: *Block,
  28229     src: LazySrcLoc,
  28230     array_ptr_src: LazySrcLoc,
  28231     array_ptr: Air.Inst.Ref,
  28232     elem_index_src: LazySrcLoc,
  28233     elem_index: Air.Inst.Ref,
  28234     init: bool,
  28235     oob_safety: bool,
  28236 ) CompileError!Air.Inst.Ref {
  28237     const pt = sema.pt;
  28238     const zcu = pt.zcu;
  28239     const array_ptr_ty = sema.typeOf(array_ptr);
  28240     const array_ty = array_ptr_ty.childType(zcu);
  28241     const array_sent = array_ty.sentinel(zcu) != null;
  28242     const array_len = array_ty.arrayLen(zcu);
  28243     const array_len_s = array_len + @intFromBool(array_sent);
  28244 
  28245     if (array_len_s == 0) {
  28246         return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{});
  28247     }
  28248 
  28249     const maybe_undef_array_ptr_val = try sema.resolveValue(array_ptr);
  28250     // The index must not be undefined since it can be out of bounds.
  28251     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  28252         const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt));
  28253         if (index >= array_len_s) {
  28254             const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
  28255             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  28256         }
  28257         break :o index;
  28258     } else null;
  28259 
  28260     const elem_ptr_ty = try array_ptr_ty.elemPtrType(offset, pt);
  28261 
  28262     if (maybe_undef_array_ptr_val) |array_ptr_val| {
  28263         if (array_ptr_val.isUndef(zcu)) {
  28264             return pt.undefRef(elem_ptr_ty);
  28265         }
  28266         if (offset) |index| {
  28267             const elem_ptr = try array_ptr_val.ptrElem(index, pt);
  28268             return Air.internedToRef(elem_ptr.toIntern());
  28269         }
  28270     }
  28271 
  28272     if (!init) {
  28273         try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(zcu), array_ty, array_ptr_src);
  28274         try sema.validateRuntimeValue(block, array_ptr_src, array_ptr);
  28275     }
  28276 
  28277     if (offset == null and array_ty.zigTypeTag(zcu) == .vector) {
  28278         return sema.fail(block, elem_index_src, "vector index not comptime known", .{});
  28279     }
  28280 
  28281     // Runtime check is only needed if unable to comptime check.
  28282     if (oob_safety and block.wantSafety() and offset == null) {
  28283         const len_inst = try pt.intRef(.usize, array_len);
  28284         const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
  28285         try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
  28286     }
  28287 
  28288     return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
  28289 }
  28290 
  28291 fn elemValSlice(
  28292     sema: *Sema,
  28293     block: *Block,
  28294     src: LazySrcLoc,
  28295     slice_src: LazySrcLoc,
  28296     slice: Air.Inst.Ref,
  28297     elem_index_src: LazySrcLoc,
  28298     elem_index: Air.Inst.Ref,
  28299     oob_safety: bool,
  28300 ) CompileError!Air.Inst.Ref {
  28301     const pt = sema.pt;
  28302     const zcu = pt.zcu;
  28303     const slice_ty = sema.typeOf(slice);
  28304     const slice_sent = slice_ty.sentinel(zcu) != null;
  28305     const elem_ty = slice_ty.elemType2(zcu);
  28306     var runtime_src = slice_src;
  28307 
  28308     // slice must be defined since it can dereferenced as null
  28309     const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
  28310     // index must be defined since it can index out of bounds
  28311     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  28312 
  28313     if (maybe_slice_val) |slice_val| {
  28314         runtime_src = elem_index_src;
  28315         const slice_len = try slice_val.sliceLen(pt);
  28316         const slice_len_s = slice_len + @intFromBool(slice_sent);
  28317         if (slice_len_s == 0) {
  28318             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  28319         }
  28320         if (maybe_index_val) |index_val| {
  28321             const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  28322             if (index >= slice_len_s) {
  28323                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  28324                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  28325             }
  28326             const elem_ptr_ty = try slice_ty.elemPtrType(index, pt);
  28327             const elem_ptr_val = try slice_val.ptrElem(index, pt);
  28328             if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
  28329                 return Air.internedToRef(elem_val.toIntern());
  28330             }
  28331             runtime_src = slice_src;
  28332         }
  28333     }
  28334 
  28335     if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| {
  28336         return Air.internedToRef(elem_only_value.toIntern());
  28337     }
  28338 
  28339     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src);
  28340     try sema.validateRuntimeValue(block, slice_src, slice);
  28341 
  28342     if (oob_safety and block.wantSafety()) {
  28343         const len_inst = if (maybe_slice_val) |slice_val|
  28344             try pt.intRef(.usize, try slice_val.sliceLen(pt))
  28345         else
  28346             try block.addTyOp(.slice_len, .usize, slice);
  28347         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  28348         try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
  28349     }
  28350     return block.addBinOp(.slice_elem_val, slice, elem_index);
  28351 }
  28352 
  28353 fn elemPtrSlice(
  28354     sema: *Sema,
  28355     block: *Block,
  28356     src: LazySrcLoc,
  28357     slice_src: LazySrcLoc,
  28358     slice: Air.Inst.Ref,
  28359     elem_index_src: LazySrcLoc,
  28360     elem_index: Air.Inst.Ref,
  28361     oob_safety: bool,
  28362 ) CompileError!Air.Inst.Ref {
  28363     const pt = sema.pt;
  28364     const zcu = pt.zcu;
  28365     const slice_ty = sema.typeOf(slice);
  28366     const slice_sent = slice_ty.sentinel(zcu) != null;
  28367 
  28368     const maybe_undef_slice_val = try sema.resolveValue(slice);
  28369     // The index must not be undefined since it can be out of bounds.
  28370     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  28371         const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt));
  28372         break :o index;
  28373     } else null;
  28374 
  28375     const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt);
  28376 
  28377     if (maybe_undef_slice_val) |slice_val| {
  28378         if (slice_val.isUndef(zcu)) {
  28379             return pt.undefRef(elem_ptr_ty);
  28380         }
  28381         const slice_len = try slice_val.sliceLen(pt);
  28382         const slice_len_s = slice_len + @intFromBool(slice_sent);
  28383         if (slice_len_s == 0) {
  28384             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  28385         }
  28386         if (offset) |index| {
  28387             if (index >= slice_len_s) {
  28388                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  28389                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  28390             }
  28391             const elem_ptr_val = try slice_val.ptrElem(index, pt);
  28392             return Air.internedToRef(elem_ptr_val.toIntern());
  28393         }
  28394     }
  28395 
  28396     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src);
  28397     try sema.validateRuntimeValue(block, slice_src, slice);
  28398 
  28399     if (oob_safety and block.wantSafety()) {
  28400         const len_inst = len: {
  28401             if (maybe_undef_slice_val) |slice_val|
  28402                 if (!slice_val.isUndef(zcu))
  28403                     break :len try pt.intRef(.usize, try slice_val.sliceLen(pt));
  28404             break :len try block.addTyOp(.slice_len, .usize, slice);
  28405         };
  28406         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  28407         try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
  28408     }
  28409     if (!try slice_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) {
  28410         // zero-bit child type; just extract the pointer and bitcast it
  28411         const slice_ptr = try block.addTyOp(.slice_ptr, slice_ty.slicePtrFieldType(zcu), slice);
  28412         return block.addBitCast(elem_ptr_ty, slice_ptr);
  28413     }
  28414     return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
  28415 }
  28416 
  28417 pub fn coerce(
  28418     sema: *Sema,
  28419     block: *Block,
  28420     dest_ty_unresolved: Type,
  28421     inst: Air.Inst.Ref,
  28422     inst_src: LazySrcLoc,
  28423 ) CompileError!Air.Inst.Ref {
  28424     return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
  28425         error.NotCoercible => unreachable,
  28426         else => |e| return e,
  28427     };
  28428 }
  28429 
  28430 const CoersionError = CompileError || error{
  28431     /// When coerce is called recursively, this error should be returned instead of using `fail`
  28432     /// to ensure correct types in compile errors.
  28433     NotCoercible,
  28434 };
  28435 
  28436 const CoerceOpts = struct {
  28437     /// Should coerceExtra emit error messages.
  28438     report_err: bool = true,
  28439     /// Ignored if `report_err == false`.
  28440     is_ret: bool = false,
  28441     /// Should coercion to comptime_int emit an error message.
  28442     no_cast_to_comptime_int: bool = false,
  28443 
  28444     param_src: struct {
  28445         func_inst: Air.Inst.Ref = .none,
  28446         param_i: u32 = undefined,
  28447 
  28448         fn get(info: @This(), sema: *Sema) !?LazySrcLoc {
  28449             if (info.func_inst == .none) return null;
  28450             const func_inst = try sema.funcDeclSrcInst(info.func_inst) orelse return null;
  28451             return .{
  28452                 .base_node_inst = func_inst,
  28453                 .offset = .{ .fn_proto_param_type = .{
  28454                     .fn_proto_node_offset = .zero,
  28455                     .param_index = info.param_i,
  28456                 } },
  28457             };
  28458         }
  28459     } = .{ .func_inst = .none, .param_i = undefined },
  28460 };
  28461 
  28462 fn coerceExtra(
  28463     sema: *Sema,
  28464     block: *Block,
  28465     dest_ty: Type,
  28466     inst: Air.Inst.Ref,
  28467     inst_src: LazySrcLoc,
  28468     opts: CoerceOpts,
  28469 ) CoersionError!Air.Inst.Ref {
  28470     if (dest_ty.isGenericPoison()) return inst;
  28471     const pt = sema.pt;
  28472     const zcu = pt.zcu;
  28473     const ip = &zcu.intern_pool;
  28474     const dest_ty_src = inst_src; // TODO better source location
  28475     try dest_ty.resolveFields(pt);
  28476     const inst_ty = sema.typeOf(inst);
  28477     try inst_ty.resolveFields(pt);
  28478     const target = zcu.getTarget();
  28479     // If the types are the same, we can return the operand.
  28480     if (dest_ty.eql(inst_ty, zcu))
  28481         return inst;
  28482 
  28483     const maybe_inst_val = try sema.resolveValue(inst);
  28484 
  28485     var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
  28486     if (in_memory_result == .ok) {
  28487         if (maybe_inst_val) |val| {
  28488             return sema.coerceInMemory(val, dest_ty);
  28489         }
  28490         try sema.requireRuntimeBlock(block, inst_src, null);
  28491         const new_val = try block.addBitCast(dest_ty, inst);
  28492         try sema.checkKnownAllocPtr(block, inst, new_val);
  28493         return new_val;
  28494     }
  28495 
  28496     switch (dest_ty.zigTypeTag(zcu)) {
  28497         .optional => optional: {
  28498             if (maybe_inst_val) |val| {
  28499                 // undefined sets the optional bit also to undefined.
  28500                 if (val.toIntern() == .undef) {
  28501                     return pt.undefRef(dest_ty);
  28502                 }
  28503 
  28504                 // null to ?T
  28505                 if (val.toIntern() == .null_value) {
  28506                     return Air.internedToRef((try pt.intern(.{ .opt = .{
  28507                         .ty = dest_ty.toIntern(),
  28508                         .val = .none,
  28509                     } })));
  28510                 }
  28511             }
  28512 
  28513             // cast from ?*T and ?[*]T to ?*anyopaque
  28514             // but don't do it if the source type is a double pointer
  28515             if (dest_ty.isPtrLikeOptional(zcu) and
  28516                 dest_ty.elemType2(zcu).toIntern() == .anyopaque_type and
  28517                 inst_ty.isPtrAtRuntime(zcu))
  28518             anyopaque_check: {
  28519                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional;
  28520                 const elem_ty = inst_ty.elemType2(zcu);
  28521                 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) {
  28522                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  28523                         .actual = inst_ty,
  28524                         .wanted = dest_ty,
  28525                     } };
  28526                     break :optional;
  28527                 }
  28528                 // Let the logic below handle wrapping the optional now that
  28529                 // it has been checked to correctly coerce.
  28530                 if (!inst_ty.isPtrLikeOptional(zcu)) break :anyopaque_check;
  28531                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28532             }
  28533 
  28534             // T to ?T
  28535             const child_type = dest_ty.optionalChild(zcu);
  28536             const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  28537                 error.NotCoercible => {
  28538                     if (in_memory_result == .no_match) {
  28539                         // Try to give more useful notes
  28540                         in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
  28541                     }
  28542                     break :optional;
  28543                 },
  28544                 else => |e| return e,
  28545             };
  28546             return try sema.wrapOptional(block, dest_ty, intermediate, inst_src);
  28547         },
  28548         .pointer => pointer: {
  28549             const dest_info = dest_ty.ptrInfo(zcu);
  28550 
  28551             // Function body to function pointer.
  28552             if (inst_ty.zigTypeTag(zcu) == .@"fn") {
  28553                 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
  28554                 const fn_nav = switch (zcu.intern_pool.indexToKey(fn_val.toIntern())) {
  28555                     .func => |f| f.owner_nav,
  28556                     .@"extern" => |e| e.owner_nav,
  28557                     else => unreachable,
  28558                 };
  28559                 const inst_as_ptr = try sema.analyzeNavRef(block, inst_src, fn_nav);
  28560                 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
  28561             }
  28562 
  28563             // *T to *[1]T
  28564             single_item: {
  28565                 if (dest_info.flags.size != .one) break :single_item;
  28566                 if (!inst_ty.isSinglePointer(zcu)) break :single_item;
  28567                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  28568                 const ptr_elem_ty = inst_ty.childType(zcu);
  28569                 const array_ty: Type = .fromInterned(dest_info.child);
  28570                 if (array_ty.zigTypeTag(zcu) != .array) break :single_item;
  28571                 const array_elem_ty = array_ty.childType(zcu);
  28572                 if (array_ty.arrayLen(zcu) != 1) break :single_item;
  28573                 const dest_is_mut = !dest_info.flags.is_const;
  28574                 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) {
  28575                     .ok => {},
  28576                     else => break :single_item,
  28577                 }
  28578                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28579             }
  28580 
  28581             // Coercions where the source is a single pointer to an array.
  28582             src_array_ptr: {
  28583                 if (!inst_ty.isSinglePointer(zcu)) break :src_array_ptr;
  28584                 if (dest_info.flags.size == .one) break :src_array_ptr; // `*[n]T` -> `*T` isn't valid
  28585                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  28586                 const array_ty = inst_ty.childType(zcu);
  28587                 if (array_ty.zigTypeTag(zcu) != .array) break :src_array_ptr;
  28588                 const array_elem_type = array_ty.childType(zcu);
  28589                 const dest_is_mut = !dest_info.flags.is_const;
  28590 
  28591                 const dst_elem_type: Type = .fromInterned(dest_info.child);
  28592                 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val);
  28593                 switch (elem_res) {
  28594                     .ok => {},
  28595                     else => {
  28596                         in_memory_result = .{ .ptr_child = .{
  28597                             .child = try elem_res.dupe(sema.arena),
  28598                             .actual = array_elem_type,
  28599                             .wanted = dst_elem_type,
  28600                         } };
  28601                         break :src_array_ptr;
  28602                     },
  28603                 }
  28604 
  28605                 if (dest_info.sentinel != .none) {
  28606                     if (array_ty.sentinel(zcu)) |inst_sent| {
  28607                         if (Air.internedToRef(dest_info.sentinel) !=
  28608                             try sema.coerceInMemory(inst_sent, dst_elem_type))
  28609                         {
  28610                             in_memory_result = .{ .ptr_sentinel = .{
  28611                                 .actual = inst_sent,
  28612                                 .wanted = Value.fromInterned(dest_info.sentinel),
  28613                                 .ty = dst_elem_type,
  28614                             } };
  28615                             break :src_array_ptr;
  28616                         }
  28617                     } else {
  28618                         in_memory_result = .{ .ptr_sentinel = .{
  28619                             .actual = Value.@"unreachable",
  28620                             .wanted = Value.fromInterned(dest_info.sentinel),
  28621                             .ty = dst_elem_type,
  28622                         } };
  28623                         break :src_array_ptr;
  28624                     }
  28625                 }
  28626 
  28627                 switch (dest_info.flags.size) {
  28628                     .slice => {
  28629                         // *[N]T to []T
  28630                         return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
  28631                     },
  28632                     .c => {
  28633                         // *[N]T to [*c]T
  28634                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28635                     },
  28636                     .many => {
  28637                         // *[N]T to [*]T
  28638                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28639                     },
  28640                     .one => unreachable, // early exit at top of block
  28641                 }
  28642             }
  28643 
  28644             // coercion from C pointer
  28645             if (inst_ty.isCPtr(zcu)) src_c_ptr: {
  28646                 if (dest_info.flags.size == .slice) break :src_c_ptr;
  28647                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr;
  28648                 // In this case we must add a safety check because the C pointer
  28649                 // could be null.
  28650                 const src_elem_ty = inst_ty.childType(zcu);
  28651                 const dest_is_mut = !dest_info.flags.is_const;
  28652                 const dst_elem_type: Type = .fromInterned(dest_info.child);
  28653                 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) {
  28654                     .ok => {},
  28655                     else => break :src_c_ptr,
  28656                 }
  28657                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28658             }
  28659 
  28660             // cast from *T and [*]T to *anyopaque
  28661             // but don't do it if the source type is a double pointer
  28662             if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(zcu) == .pointer) to_anyopaque: {
  28663                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  28664                 const elem_ty = inst_ty.elemType2(zcu);
  28665                 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) {
  28666                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  28667                         .actual = inst_ty,
  28668                         .wanted = dest_ty,
  28669                     } };
  28670                     break :pointer;
  28671                 }
  28672                 if (dest_ty.isSlice(zcu)) break :to_anyopaque;
  28673                 if (inst_ty.isSlice(zcu)) {
  28674                     in_memory_result = .{ .slice_to_anyopaque = .{
  28675                         .actual = inst_ty,
  28676                         .wanted = dest_ty,
  28677                     } };
  28678                     break :pointer;
  28679                 }
  28680                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28681             }
  28682 
  28683             switch (dest_info.flags.size) {
  28684                 // coercion to C pointer
  28685                 .c => switch (inst_ty.zigTypeTag(zcu)) {
  28686                     .null => return Air.internedToRef(try pt.intern(.{ .ptr = .{
  28687                         .ty = dest_ty.toIntern(),
  28688                         .base_addr = .int,
  28689                         .byte_offset = 0,
  28690                     } })),
  28691                     .comptime_int => {
  28692                         const addr = sema.coerceExtra(block, .usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  28693                             error.NotCoercible => break :pointer,
  28694                             else => |e| return e,
  28695                         };
  28696                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  28697                     },
  28698                     .int => {
  28699                         const ptr_size_ty: Type = switch (inst_ty.intInfo(zcu).signedness) {
  28700                             .signed => .isize,
  28701                             .unsigned => .usize,
  28702                         };
  28703                         const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  28704                             error.NotCoercible => {
  28705                                 // Try to give more useful notes
  28706                                 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
  28707                                 break :pointer;
  28708                             },
  28709                             else => |e| return e,
  28710                         };
  28711                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  28712                     },
  28713                     .pointer => p: {
  28714                         if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  28715                         const inst_info = inst_ty.ptrInfo(zcu);
  28716                         switch (try sema.coerceInMemoryAllowed(
  28717                             block,
  28718                             .fromInterned(dest_info.child),
  28719                             .fromInterned(inst_info.child),
  28720                             !dest_info.flags.is_const,
  28721                             target,
  28722                             dest_ty_src,
  28723                             inst_src,
  28724                             maybe_inst_val,
  28725                         )) {
  28726                             .ok => {},
  28727                             else => break :p,
  28728                         }
  28729                         if (inst_info.flags.size == .slice) {
  28730                             assert(dest_info.sentinel == .none);
  28731                             if (inst_info.sentinel == .none or
  28732                                 inst_info.sentinel != (try pt.intValue(.fromInterned(inst_info.child), 0)).toIntern())
  28733                                 break :p;
  28734 
  28735                             const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  28736                             return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  28737                         }
  28738                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28739                     },
  28740                     else => {},
  28741                 },
  28742                 .one => {},
  28743                 .slice => to_slice: {
  28744                     if (inst_ty.zigTypeTag(zcu) == .array) {
  28745                         return sema.fail(
  28746                             block,
  28747                             inst_src,
  28748                             "array literal requires address-of operator (&) to coerce to slice type '{f}'",
  28749                             .{dest_ty.fmt(pt)},
  28750                         );
  28751                     }
  28752 
  28753                     if (!inst_ty.isSinglePointer(zcu)) break :to_slice;
  28754                     const inst_child_ty = inst_ty.childType(zcu);
  28755                     if (!inst_child_ty.isTuple(zcu)) break :to_slice;
  28756 
  28757                     // empty tuple to zero-length slice
  28758                     // note that this allows coercing to a mutable slice.
  28759                     if (inst_child_ty.structFieldCount(zcu) == 0) {
  28760                         const align_val = try dest_ty.ptrAlignmentSema(pt);
  28761                         return Air.internedToRef(try pt.intern(.{ .slice = .{
  28762                             .ty = dest_ty.toIntern(),
  28763                             .ptr = try pt.intern(.{ .ptr = .{
  28764                                 .ty = dest_ty.slicePtrFieldType(zcu).toIntern(),
  28765                                 .base_addr = .int,
  28766                                 .byte_offset = align_val.toByteUnits().?,
  28767                             } }),
  28768                             .len = .zero_usize,
  28769                         } }));
  28770                     }
  28771 
  28772                     // pointer to tuple to slice
  28773                     if (!dest_info.flags.is_const) {
  28774                         const err_msg = err_msg: {
  28775                             const err_msg = try sema.errMsg(inst_src, "cannot cast pointer to tuple to '{f}'", .{dest_ty.fmt(pt)});
  28776                             errdefer err_msg.destroy(sema.gpa);
  28777                             try sema.errNote(dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{});
  28778                             break :err_msg err_msg;
  28779                         };
  28780                         return sema.failWithOwnedErrorMsg(block, err_msg);
  28781                     }
  28782                     return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  28783                 },
  28784                 .many => p: {
  28785                     if (!inst_ty.isSlice(zcu)) break :p;
  28786                     if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  28787                     const inst_info = inst_ty.ptrInfo(zcu);
  28788 
  28789                     switch (try sema.coerceInMemoryAllowed(
  28790                         block,
  28791                         .fromInterned(dest_info.child),
  28792                         .fromInterned(inst_info.child),
  28793                         !dest_info.flags.is_const,
  28794                         target,
  28795                         dest_ty_src,
  28796                         inst_src,
  28797                         maybe_inst_val,
  28798                     )) {
  28799                         .ok => {},
  28800                         else => break :p,
  28801                     }
  28802 
  28803                     if (dest_info.sentinel == .none or inst_info.sentinel == .none or
  28804                         Air.internedToRef(dest_info.sentinel) !=
  28805                             try sema.coerceInMemory(Value.fromInterned(inst_info.sentinel), .fromInterned(dest_info.child)))
  28806                         break :p;
  28807 
  28808                     const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  28809                     return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  28810                 },
  28811             }
  28812         },
  28813         .int, .comptime_int => switch (inst_ty.zigTypeTag(zcu)) {
  28814             .float, .comptime_float => float: {
  28815                 const val = maybe_inst_val orelse {
  28816                     if (dest_ty.zigTypeTag(zcu) == .comptime_int) {
  28817                         if (!opts.report_err) return error.NotCoercible;
  28818                         return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int });
  28819                     }
  28820                     break :float;
  28821                 };
  28822                 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact);
  28823                 return Air.internedToRef(result_val.toIntern());
  28824             },
  28825             .int, .comptime_int => {
  28826                 if (maybe_inst_val) |val| {
  28827                     // comptime-known integer to other number
  28828                     if (!(try sema.intFitsInType(val, dest_ty, null))) {
  28829                         if (!opts.report_err) return error.NotCoercible;
  28830                         return sema.fail(block, inst_src, "type '{f}' cannot represent integer value '{f}'", .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) });
  28831                     }
  28832                     return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  28833                         .undef => try pt.undefRef(dest_ty),
  28834                         .int => |int| Air.internedToRef(
  28835                             try zcu.intern_pool.getCoercedInts(zcu.gpa, pt.tid, int, dest_ty.toIntern()),
  28836                         ),
  28837                         else => unreachable,
  28838                     };
  28839                 }
  28840                 if (dest_ty.zigTypeTag(zcu) == .comptime_int) {
  28841                     if (!opts.report_err) return error.NotCoercible;
  28842                     if (opts.no_cast_to_comptime_int) return inst;
  28843                     return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int });
  28844                 }
  28845 
  28846                 // integer widening
  28847                 const dst_info = dest_ty.intInfo(zcu);
  28848                 const src_info = inst_ty.intInfo(zcu);
  28849                 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
  28850                     // small enough unsigned ints can get casted to large enough signed ints
  28851                     (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
  28852                 {
  28853                     try sema.requireRuntimeBlock(block, inst_src, null);
  28854                     return block.addTyOp(.intcast, dest_ty, inst);
  28855                 }
  28856             },
  28857             else => {},
  28858         },
  28859         .float, .comptime_float => switch (inst_ty.zigTypeTag(zcu)) {
  28860             .comptime_float => {
  28861                 const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
  28862                 const result_val = try val.floatCast(dest_ty, pt);
  28863                 return Air.internedToRef(result_val.toIntern());
  28864             },
  28865             .float => {
  28866                 if (maybe_inst_val) |val| {
  28867                     const result_val = try val.floatCast(dest_ty, pt);
  28868                     if (!val.eql(try result_val.floatCast(inst_ty, pt), inst_ty, zcu)) {
  28869                         return sema.fail(
  28870                             block,
  28871                             inst_src,
  28872                             "type '{f}' cannot represent float value '{f}'",
  28873                             .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) },
  28874                         );
  28875                     }
  28876                     return Air.internedToRef(result_val.toIntern());
  28877                 } else if (dest_ty.zigTypeTag(zcu) == .comptime_float) {
  28878                     if (!opts.report_err) return error.NotCoercible;
  28879                     return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float });
  28880                 }
  28881 
  28882                 // float widening
  28883                 const src_bits = inst_ty.floatBits(target);
  28884                 const dst_bits = dest_ty.floatBits(target);
  28885                 if (dst_bits >= src_bits) {
  28886                     try sema.requireRuntimeBlock(block, inst_src, null);
  28887                     return block.addTyOp(.fpext, dest_ty, inst);
  28888                 }
  28889             },
  28890             .int, .comptime_int => int: {
  28891                 const val = maybe_inst_val orelse {
  28892                     if (dest_ty.zigTypeTag(zcu) == .comptime_float) {
  28893                         if (!opts.report_err) return error.NotCoercible;
  28894                         return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float });
  28895                     }
  28896                     break :int;
  28897                 };
  28898                 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema);
  28899                 const fits: bool = switch (ip.indexToKey(result_val.toIntern())) {
  28900                     else => unreachable,
  28901                     .undef => true,
  28902                     .float => |float| fits: {
  28903                         var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  28904                         const operand_big_int = val.toBigInt(&buffer, zcu);
  28905                         switch (float.storage) {
  28906                             inline else => |x| {
  28907                                 if (!std.math.isFinite(x)) break :fits false;
  28908                                 var result_big_int: std.math.big.int.Mutable = .{
  28909                                     .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)),
  28910                                     .len = undefined,
  28911                                     .positive = undefined,
  28912                                 };
  28913                                 switch (result_big_int.setFloat(x, .nearest_even)) {
  28914                                     .inexact => break :fits false,
  28915                                     .exact => {},
  28916                                 }
  28917                                 break :fits result_big_int.toConst().eql(operand_big_int);
  28918                             },
  28919                         }
  28920                     },
  28921                 };
  28922                 if (!fits) return sema.fail(
  28923                     block,
  28924                     inst_src,
  28925                     "type '{f}' cannot represent integer value '{f}'",
  28926                     .{ dest_ty.fmt(pt), val.fmtValue(pt) },
  28927                 );
  28928                 return .fromValue(result_val);
  28929             },
  28930             else => {},
  28931         },
  28932         .@"enum" => switch (inst_ty.zigTypeTag(zcu)) {
  28933             .enum_literal => {
  28934                 // enum literal to enum
  28935                 const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
  28936                 const string = zcu.intern_pool.indexToKey(val.toIntern()).enum_literal;
  28937                 const field_index = dest_ty.enumFieldIndex(string, zcu) orelse {
  28938                     return sema.fail(block, inst_src, "no field named '{f}' in enum '{f}'", .{
  28939                         string.fmt(&zcu.intern_pool), dest_ty.fmt(pt),
  28940                     });
  28941                 };
  28942                 return Air.internedToRef((try pt.enumValueFieldIndex(dest_ty, @intCast(field_index))).toIntern());
  28943             },
  28944             .@"union" => blk: {
  28945                 // union to its own tag type
  28946                 const union_tag_ty = inst_ty.unionTagType(zcu) orelse break :blk;
  28947                 if (union_tag_ty.eql(dest_ty, zcu)) {
  28948                     return sema.unionToTag(block, dest_ty, inst, inst_src);
  28949                 }
  28950             },
  28951             else => {},
  28952         },
  28953         .error_union => switch (inst_ty.zigTypeTag(zcu)) {
  28954             .error_union => eu: {
  28955                 if (maybe_inst_val) |inst_val| {
  28956                     switch (inst_val.toIntern()) {
  28957                         .undef => return pt.undefRef(dest_ty),
  28958                         else => switch (zcu.intern_pool.indexToKey(inst_val.toIntern())) {
  28959                             .error_union => |error_union| switch (error_union.val) {
  28960                                 .err_name => |err_name| {
  28961                                     const error_set_ty = inst_ty.errorUnionSet(zcu);
  28962                                     const error_set_val = Air.internedToRef((try pt.intern(.{ .err = .{
  28963                                         .ty = error_set_ty.toIntern(),
  28964                                         .name = err_name,
  28965                                     } })));
  28966                                     return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src);
  28967                                 },
  28968                                 .payload => |payload| {
  28969                                     const payload_val = Air.internedToRef(payload);
  28970                                     return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) {
  28971                                         error.NotCoercible => break :eu,
  28972                                         else => |e| return e,
  28973                                     };
  28974                                 },
  28975                             },
  28976                             else => unreachable,
  28977                         },
  28978                     }
  28979                 }
  28980             },
  28981             .error_set => {
  28982                 // E to E!T
  28983                 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
  28984             },
  28985             else => eu: {
  28986                 // T to E!T
  28987                 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  28988                     error.NotCoercible => {
  28989                         if (in_memory_result == .no_match) {
  28990                             const payload_type = dest_ty.errorUnionPayload(zcu);
  28991                             // Try to give more useful notes
  28992                             in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
  28993                         }
  28994                         break :eu;
  28995                     },
  28996                     else => |e| return e,
  28997                 };
  28998             },
  28999         },
  29000         .@"union" => switch (inst_ty.zigTypeTag(zcu)) {
  29001             .@"enum", .enum_literal => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
  29002             else => {},
  29003         },
  29004         .array => switch (inst_ty.zigTypeTag(zcu)) {
  29005             .array => array_to_array: {
  29006                 // Array coercions are allowed only if the child is IMC and the sentinel is unchanged or removed.
  29007                 if (.ok != try sema.coerceInMemoryAllowed(
  29008                     block,
  29009                     dest_ty.childType(zcu),
  29010                     inst_ty.childType(zcu),
  29011                     false,
  29012                     target,
  29013                     dest_ty_src,
  29014                     inst_src,
  29015                     maybe_inst_val,
  29016                 )) {
  29017                     break :array_to_array;
  29018                 }
  29019 
  29020                 if (dest_ty.sentinel(zcu)) |dest_sent| {
  29021                     const src_sent = inst_ty.sentinel(zcu) orelse break :array_to_array;
  29022                     if (dest_sent.toIntern() != (try pt.getCoerced(src_sent, dest_ty.childType(zcu))).toIntern()) {
  29023                         break :array_to_array;
  29024                     }
  29025                 }
  29026 
  29027                 return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src);
  29028             },
  29029             .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  29030             .@"struct" => {
  29031                 if (inst_ty.isTuple(zcu)) {
  29032                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  29033                 }
  29034             },
  29035             else => {},
  29036         },
  29037         .vector => switch (inst_ty.zigTypeTag(zcu)) {
  29038             .array, .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  29039             .@"struct" => {
  29040                 if (inst_ty.isTuple(zcu)) {
  29041                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  29042                 }
  29043             },
  29044             else => {},
  29045         },
  29046         .@"struct" => blk: {
  29047             if (dest_ty.isTuple(zcu) and inst_ty.isTuple(zcu)) {
  29048                 return sema.coerceTupleToTuple(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  29049                     error.NotCoercible => break :blk,
  29050                     else => |e| return e,
  29051                 };
  29052             }
  29053         },
  29054         else => {},
  29055     }
  29056 
  29057     const can_coerce_to = switch (dest_ty.zigTypeTag(zcu)) {
  29058         .noreturn, .@"opaque" => false,
  29059         else => true,
  29060     };
  29061 
  29062     if (can_coerce_to) {
  29063         // undefined to anything. We do this after the big switch above so that
  29064         // special logic has a chance to run first, such as `*[N]T` to `[]T` which
  29065         // should initialize the length field of the slice.
  29066         if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty);
  29067     }
  29068 
  29069     if (!opts.report_err) return error.NotCoercible;
  29070 
  29071     if (opts.is_ret and dest_ty.zigTypeTag(zcu) == .noreturn) {
  29072         const msg = msg: {
  29073             const msg = try sema.errMsg(inst_src, "function declared 'noreturn' returns", .{});
  29074             errdefer msg.destroy(sema.gpa);
  29075 
  29076             const ret_ty_src: LazySrcLoc = .{
  29077                 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip),
  29078                 .offset = .{ .node_offset_fn_type_ret_ty = .zero },
  29079             };
  29080             try sema.errNote(ret_ty_src, msg, "'noreturn' declared here", .{});
  29081             break :msg msg;
  29082         };
  29083         return sema.failWithOwnedErrorMsg(block, msg);
  29084     }
  29085 
  29086     const msg = msg: {
  29087         const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), inst_ty.fmt(pt) });
  29088         errdefer msg.destroy(sema.gpa);
  29089 
  29090         if (!can_coerce_to) {
  29091             try sema.errNote(inst_src, msg, "cannot coerce to '{f}'", .{dest_ty.fmt(pt)});
  29092         }
  29093 
  29094         // E!T to T
  29095         if (inst_ty.zigTypeTag(zcu) == .error_union and
  29096             (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok)
  29097         {
  29098             try sema.errNote(inst_src, msg, "cannot convert error union to payload type", .{});
  29099             try sema.errNote(inst_src, msg, "consider using 'try', 'catch', or 'if'", .{});
  29100         }
  29101 
  29102         // ?T to T
  29103         if (inst_ty.zigTypeTag(zcu) == .optional and
  29104             (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok)
  29105         {
  29106             try sema.errNote(inst_src, msg, "cannot convert optional to payload type", .{});
  29107             try sema.errNote(inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
  29108         }
  29109 
  29110         try in_memory_result.report(sema, inst_src, msg);
  29111 
  29112         // Add notes about function return type
  29113         if (opts.is_ret and
  29114             !zcu.test_functions.contains(zcu.funcInfo(sema.func_index).owner_nav))
  29115         {
  29116             const ret_ty_src: LazySrcLoc = .{
  29117                 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip),
  29118                 .offset = .{ .node_offset_fn_type_ret_ty = .zero },
  29119             };
  29120             if (inst_ty.isError(zcu) and !dest_ty.isError(zcu)) {
  29121                 try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{});
  29122             } else {
  29123                 try sema.errNote(ret_ty_src, msg, "function return type declared here", .{});
  29124             }
  29125         }
  29126 
  29127         if (try opts.param_src.get(sema)) |param_src| {
  29128             try sema.errNote(param_src, msg, "parameter type declared here", .{});
  29129         }
  29130 
  29131         // TODO maybe add "cannot store an error in type '{f}'" note
  29132 
  29133         break :msg msg;
  29134     };
  29135     return sema.failWithOwnedErrorMsg(block, msg);
  29136 }
  29137 
  29138 fn coerceInMemory(
  29139     sema: *Sema,
  29140     val: Value,
  29141     dst_ty: Type,
  29142 ) CompileError!Air.Inst.Ref {
  29143     return Air.internedToRef((try sema.pt.getCoerced(val, dst_ty)).toIntern());
  29144 }
  29145 
  29146 const InMemoryCoercionResult = union(enum) {
  29147     ok,
  29148     no_match: Pair,
  29149     int_not_coercible: Int,
  29150     comptime_int_not_coercible: TypeValuePair,
  29151     error_union_payload: PairAndChild,
  29152     array_len: IntPair,
  29153     array_sentinel: Sentinel,
  29154     array_elem: PairAndChild,
  29155     vector_len: IntPair,
  29156     vector_elem: PairAndChild,
  29157     optional_shape: Pair,
  29158     optional_child: PairAndChild,
  29159     from_anyerror,
  29160     missing_error: []const InternPool.NullTerminatedString,
  29161     /// true if wanted is var args
  29162     fn_var_args: bool,
  29163     /// true if wanted is generic
  29164     fn_generic: bool,
  29165     fn_param_count: IntPair,
  29166     fn_param_noalias: IntPair,
  29167     fn_param_comptime: ComptimeParam,
  29168     fn_param: Param,
  29169     fn_cc: CC,
  29170     fn_return_type: PairAndChild,
  29171     ptr_child: PairAndChild,
  29172     ptr_addrspace: AddressSpace,
  29173     ptr_sentinel: Sentinel,
  29174     ptr_size: Size,
  29175     ptr_const: Pair,
  29176     ptr_volatile: Pair,
  29177     ptr_allowzero: Pair,
  29178     ptr_bit_range: BitRange,
  29179     ptr_alignment: AlignPair,
  29180     double_ptr_to_anyopaque: Pair,
  29181     slice_to_anyopaque: Pair,
  29182 
  29183     const Pair = struct {
  29184         actual: Type,
  29185         wanted: Type,
  29186     };
  29187 
  29188     const TypeValuePair = struct {
  29189         actual: Value,
  29190         wanted: Type,
  29191     };
  29192 
  29193     const PairAndChild = struct {
  29194         child: *InMemoryCoercionResult,
  29195         actual: Type,
  29196         wanted: Type,
  29197     };
  29198 
  29199     const Param = struct {
  29200         child: *InMemoryCoercionResult,
  29201         actual: Type,
  29202         wanted: Type,
  29203         index: u64,
  29204     };
  29205 
  29206     const ComptimeParam = struct {
  29207         index: u64,
  29208         wanted: bool,
  29209     };
  29210 
  29211     const Sentinel = struct {
  29212         // unreachable_value indicates no sentinel
  29213         actual: Value,
  29214         wanted: Value,
  29215         ty: Type,
  29216     };
  29217 
  29218     const Int = struct {
  29219         actual_signedness: std.builtin.Signedness,
  29220         wanted_signedness: std.builtin.Signedness,
  29221         actual_bits: u16,
  29222         wanted_bits: u16,
  29223     };
  29224 
  29225     const IntPair = struct {
  29226         actual: u64,
  29227         wanted: u64,
  29228     };
  29229 
  29230     const AlignPair = struct {
  29231         actual: Alignment,
  29232         wanted: Alignment,
  29233     };
  29234 
  29235     const Size = struct {
  29236         actual: std.builtin.Type.Pointer.Size,
  29237         wanted: std.builtin.Type.Pointer.Size,
  29238     };
  29239 
  29240     const AddressSpace = struct {
  29241         actual: std.builtin.AddressSpace,
  29242         wanted: std.builtin.AddressSpace,
  29243     };
  29244 
  29245     const CC = struct {
  29246         actual: std.builtin.CallingConvention,
  29247         wanted: std.builtin.CallingConvention,
  29248     };
  29249 
  29250     const BitRange = struct {
  29251         actual_host: u16,
  29252         wanted_host: u16,
  29253         actual_offset: u16,
  29254         wanted_offset: u16,
  29255     };
  29256 
  29257     fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult {
  29258         const res = try arena.create(InMemoryCoercionResult);
  29259         res.* = child.*;
  29260         return res;
  29261     }
  29262 
  29263     fn report(res: *const InMemoryCoercionResult, sema: *Sema, src: LazySrcLoc, msg: *Zcu.ErrorMsg) !void {
  29264         const pt = sema.pt;
  29265         var cur = res;
  29266         while (true) switch (cur.*) {
  29267             .ok => unreachable,
  29268             .no_match => |types| {
  29269                 try sema.addDeclaredHereNote(msg, types.wanted);
  29270                 try sema.addDeclaredHereNote(msg, types.actual);
  29271                 break;
  29272             },
  29273             .int_not_coercible => |int| {
  29274                 try sema.errNote(src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{
  29275                     @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits,
  29276                 });
  29277                 break;
  29278             },
  29279             .comptime_int_not_coercible => |int| {
  29280                 try sema.errNote(src, msg, "type '{f}' cannot represent value '{f}'", .{
  29281                     int.wanted.fmt(pt), int.actual.fmtValueSema(pt, sema),
  29282                 });
  29283                 break;
  29284             },
  29285             .error_union_payload => |pair| {
  29286                 try sema.errNote(src, msg, "error union payload '{f}' cannot cast into error union payload '{f}'", .{
  29287                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29288                 });
  29289                 cur = pair.child;
  29290             },
  29291             .array_len => |lens| {
  29292                 try sema.errNote(src, msg, "array of length {d} cannot cast into an array of length {d}", .{
  29293                     lens.actual, lens.wanted,
  29294                 });
  29295                 break;
  29296             },
  29297             .array_sentinel => |sentinel| {
  29298                 if (sentinel.actual.toIntern() != .unreachable_value) {
  29299                     try sema.errNote(src, msg, "array sentinel '{f}' cannot cast into array sentinel '{f}'", .{
  29300                         sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema),
  29301                     });
  29302                 } else {
  29303                     try sema.errNote(src, msg, "destination array requires '{f}' sentinel", .{
  29304                         sentinel.wanted.fmtValueSema(pt, sema),
  29305                     });
  29306                 }
  29307                 break;
  29308             },
  29309             .array_elem => |pair| {
  29310                 try sema.errNote(src, msg, "array element type '{f}' cannot cast into array element type '{f}'", .{
  29311                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29312                 });
  29313                 cur = pair.child;
  29314             },
  29315             .vector_len => |lens| {
  29316                 try sema.errNote(src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{
  29317                     lens.actual, lens.wanted,
  29318                 });
  29319                 break;
  29320             },
  29321             .vector_elem => |pair| {
  29322                 try sema.errNote(src, msg, "vector element type '{f}' cannot cast into vector element type '{f}'", .{
  29323                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29324                 });
  29325                 cur = pair.child;
  29326             },
  29327             .optional_shape => |pair| {
  29328                 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{
  29329                     pair.actual.optionalChild(pt.zcu).fmt(pt), pair.wanted.optionalChild(pt.zcu).fmt(pt),
  29330                 });
  29331                 break;
  29332             },
  29333             .optional_child => |pair| {
  29334                 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{
  29335                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29336                 });
  29337                 cur = pair.child;
  29338             },
  29339             .from_anyerror => {
  29340                 try sema.errNote(src, msg, "global error set cannot cast into a smaller set", .{});
  29341                 break;
  29342             },
  29343             .missing_error => |missing_errors| {
  29344                 for (missing_errors) |err| {
  29345                     try sema.errNote(src, msg, "'error.{f}' not a member of destination error set", .{err.fmt(&pt.zcu.intern_pool)});
  29346                 }
  29347                 break;
  29348             },
  29349             .fn_var_args => |wanted_var_args| {
  29350                 if (wanted_var_args) {
  29351                     try sema.errNote(src, msg, "non-variadic function cannot cast into a variadic function", .{});
  29352                 } else {
  29353                     try sema.errNote(src, msg, "variadic function cannot cast into a non-variadic function", .{});
  29354                 }
  29355                 break;
  29356             },
  29357             .fn_generic => |wanted_generic| {
  29358                 if (wanted_generic) {
  29359                     try sema.errNote(src, msg, "non-generic function cannot cast into a generic function", .{});
  29360                 } else {
  29361                     try sema.errNote(src, msg, "generic function cannot cast into a non-generic function", .{});
  29362                 }
  29363                 break;
  29364             },
  29365             .fn_param_count => |lens| {
  29366                 try sema.errNote(src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{
  29367                     lens.actual, lens.wanted,
  29368                 });
  29369                 break;
  29370             },
  29371             .fn_param_noalias => |param| {
  29372                 var index: u6 = 0;
  29373                 var actual_noalias = false;
  29374                 while (true) : (index += 1) {
  29375                     const actual: u1 = @truncate(param.actual >> index);
  29376                     const wanted: u1 = @truncate(param.wanted >> index);
  29377                     if (actual != wanted) {
  29378                         actual_noalias = actual == 1;
  29379                         break;
  29380                     }
  29381                 }
  29382                 if (!actual_noalias) {
  29383                     try sema.errNote(src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index});
  29384                 } else {
  29385                     try sema.errNote(src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index});
  29386                 }
  29387                 break;
  29388             },
  29389             .fn_param_comptime => |param| {
  29390                 if (param.wanted) {
  29391                     try sema.errNote(src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index});
  29392                 } else {
  29393                     try sema.errNote(src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index});
  29394                 }
  29395                 break;
  29396             },
  29397             .fn_param => |param| {
  29398                 try sema.errNote(src, msg, "parameter {d} '{f}' cannot cast into '{f}'", .{
  29399                     param.index, param.actual.fmt(pt), param.wanted.fmt(pt),
  29400                 });
  29401                 cur = param.child;
  29402             },
  29403             .fn_cc => |cc| {
  29404                 try sema.errNote(src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) });
  29405                 break;
  29406             },
  29407             .fn_return_type => |pair| {
  29408                 try sema.errNote(src, msg, "return type '{f}' cannot cast into return type '{f}'", .{
  29409                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29410                 });
  29411                 cur = pair.child;
  29412             },
  29413             .ptr_child => |pair| {
  29414                 try sema.errNote(src, msg, "pointer type child '{f}' cannot cast into pointer type child '{f}'", .{
  29415                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29416                 });
  29417                 cur = pair.child;
  29418             },
  29419             .ptr_addrspace => |@"addrspace"| {
  29420                 try sema.errNote(src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) });
  29421                 break;
  29422             },
  29423             .ptr_sentinel => |sentinel| {
  29424                 if (sentinel.actual.toIntern() != .unreachable_value) {
  29425                     try sema.errNote(src, msg, "pointer sentinel '{f}' cannot cast into pointer sentinel '{f}'", .{
  29426                         sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema),
  29427                     });
  29428                 } else {
  29429                     try sema.errNote(src, msg, "destination pointer requires '{f}' sentinel", .{
  29430                         sentinel.wanted.fmtValueSema(pt, sema),
  29431                     });
  29432                 }
  29433                 break;
  29434             },
  29435             .ptr_size => |size| {
  29436                 try sema.errNote(src, msg, "a {s} cannot cast into a {s}", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) });
  29437                 break;
  29438             },
  29439             .ptr_allowzero => |pair| {
  29440                 const wanted_allow_zero = pair.wanted.ptrAllowsZero(pt.zcu);
  29441                 const actual_allow_zero = pair.actual.ptrAllowsZero(pt.zcu);
  29442                 if (actual_allow_zero and !wanted_allow_zero) {
  29443                     try sema.errNote(src, msg, "'{f}' could have null values which are illegal in type '{f}'", .{
  29444                         pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29445                     });
  29446                 } else {
  29447                     try sema.errNote(src, msg, "mutable '{f}' would allow illegal null values stored to type '{f}'", .{
  29448                         pair.wanted.fmt(pt), pair.actual.fmt(pt),
  29449                     });
  29450                 }
  29451                 break;
  29452             },
  29453             .ptr_const => |pair| {
  29454                 const wanted_const = pair.wanted.isConstPtr(pt.zcu);
  29455                 const actual_const = pair.actual.isConstPtr(pt.zcu);
  29456                 if (actual_const and !wanted_const) {
  29457                     try sema.errNote(src, msg, "cast discards const qualifier", .{});
  29458                 } else {
  29459                     try sema.errNote(src, msg, "mutable '{f}' would allow illegal const pointers stored to type '{f}'", .{
  29460                         pair.wanted.fmt(pt), pair.actual.fmt(pt),
  29461                     });
  29462                 }
  29463                 break;
  29464             },
  29465             .ptr_volatile => |pair| {
  29466                 const wanted_volatile = pair.wanted.isVolatilePtr(pt.zcu);
  29467                 const actual_volatile = pair.actual.isVolatilePtr(pt.zcu);
  29468                 if (actual_volatile and !wanted_volatile) {
  29469                     try sema.errNote(src, msg, "cast discards volatile qualifier", .{});
  29470                 } else {
  29471                     try sema.errNote(src, msg, "mutable '{f}' would allow illegal volatile pointers stored to type '{f}'", .{
  29472                         pair.wanted.fmt(pt), pair.actual.fmt(pt),
  29473                     });
  29474                 }
  29475                 break;
  29476             },
  29477             .ptr_bit_range => |bit_range| {
  29478                 if (bit_range.actual_host != bit_range.wanted_host) {
  29479                     try sema.errNote(src, msg, "pointer host size '{d}' cannot cast into pointer host size '{d}'", .{
  29480                         bit_range.actual_host, bit_range.wanted_host,
  29481                     });
  29482                 }
  29483                 if (bit_range.actual_offset != bit_range.wanted_offset) {
  29484                     try sema.errNote(src, msg, "pointer bit offset '{d}' cannot cast into pointer bit offset '{d}'", .{
  29485                         bit_range.actual_offset, bit_range.wanted_offset,
  29486                     });
  29487                 }
  29488                 break;
  29489             },
  29490             .ptr_alignment => |pair| {
  29491                 try sema.errNote(src, msg, "pointer alignment '{d}' cannot cast into pointer alignment '{d}'", .{
  29492                     pair.actual.toByteUnits() orelse 0, pair.wanted.toByteUnits() orelse 0,
  29493                 });
  29494                 break;
  29495             },
  29496             .double_ptr_to_anyopaque => |pair| {
  29497                 try sema.errNote(src, msg, "cannot implicitly cast double pointer '{f}' to anyopaque pointer '{f}'", .{
  29498                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29499                 });
  29500                 break;
  29501             },
  29502             .slice_to_anyopaque => |pair| {
  29503                 try sema.errNote(src, msg, "cannot implicitly cast slice '{f}' to anyopaque pointer '{f}'", .{
  29504                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29505                 });
  29506                 try sema.errNote(src, msg, "consider using '.ptr'", .{});
  29507                 break;
  29508             },
  29509         };
  29510     }
  29511 };
  29512 
  29513 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 {
  29514     return switch (size) {
  29515         .one => "single pointer",
  29516         .many => "many pointer",
  29517         .c => "C pointer",
  29518         .slice => "slice",
  29519     };
  29520 }
  29521 
  29522 /// If types `A` and `B` have identical representations in runtime memory, they are considered
  29523 /// "in-memory coercible". This is a subset of normal coercions. Not only can `A` coerce to `B`, but
  29524 /// also, coercions can happen through pointers. For instance, `*const A` can coerce to `*const B`.
  29525 ///
  29526 /// If this function is called, the coercion must be applied, or a compile error emitted if `.ok`
  29527 /// is not returned. This is because this function may modify inferred error sets to make a
  29528 /// coercion possible, even if `.ok` is not returned.
  29529 pub fn coerceInMemoryAllowed(
  29530     sema: *Sema,
  29531     block: *Block,
  29532     dest_ty: Type,
  29533     src_ty: Type,
  29534     /// If `true`, this query comes from an attempted coercion of the form `*Src` -> `*Dest`, where
  29535     /// both pointers are mutable. If this coercion is allowed, one could store to the `*Dest` and
  29536     /// load from the `*Src` to effectively perform an in-memory coercion from `Dest` to `Src`.
  29537     /// Therefore, when `dest_is_mut`, the in-memory coercion must be valid in *both directions*.
  29538     dest_is_mut: bool,
  29539     target: *const std.Target,
  29540     dest_src: LazySrcLoc,
  29541     src_src: LazySrcLoc,
  29542     src_val: ?Value,
  29543 ) CompileError!InMemoryCoercionResult {
  29544     const pt = sema.pt;
  29545     const zcu = pt.zcu;
  29546 
  29547     if (dest_ty.eql(src_ty, zcu))
  29548         return .ok;
  29549 
  29550     const dest_tag = dest_ty.zigTypeTag(zcu);
  29551     const src_tag = src_ty.zigTypeTag(zcu);
  29552 
  29553     // Differently-named integers with the same number of bits.
  29554     if (dest_tag == .int and src_tag == .int) {
  29555         const dest_info = dest_ty.intInfo(zcu);
  29556         const src_info = src_ty.intInfo(zcu);
  29557 
  29558         if (dest_info.signedness == src_info.signedness and
  29559             dest_info.bits == src_info.bits)
  29560         {
  29561             return .ok;
  29562         }
  29563 
  29564         if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or
  29565             // small enough unsigned ints can get casted to large enough signed ints
  29566             (dest_info.signedness == .signed and src_info.signedness == .unsigned and dest_info.bits <= src_info.bits) or
  29567             (dest_info.signedness == .unsigned and src_info.signedness == .signed))
  29568         {
  29569             return InMemoryCoercionResult{ .int_not_coercible = .{
  29570                 .actual_signedness = src_info.signedness,
  29571                 .wanted_signedness = dest_info.signedness,
  29572                 .actual_bits = src_info.bits,
  29573                 .wanted_bits = dest_info.bits,
  29574             } };
  29575         }
  29576     }
  29577 
  29578     // Comptime int to regular int.
  29579     if (dest_tag == .int and src_tag == .comptime_int) {
  29580         if (src_val) |val| {
  29581             if (!(try sema.intFitsInType(val, dest_ty, null))) {
  29582                 return .{ .comptime_int_not_coercible = .{ .wanted = dest_ty, .actual = val } };
  29583             }
  29584         }
  29585     }
  29586 
  29587     // Differently-named floats with the same number of bits.
  29588     if (dest_tag == .float and src_tag == .float) {
  29589         const dest_bits = dest_ty.floatBits(target);
  29590         const src_bits = src_ty.floatBits(target);
  29591         if (dest_bits == src_bits) {
  29592             return .ok;
  29593         }
  29594     }
  29595 
  29596     // Pointers / Pointer-like Optionals
  29597     const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty);
  29598     const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty);
  29599     if (maybe_dest_ptr_ty) |dest_ptr_ty| {
  29600         if (maybe_src_ptr_ty) |src_ptr_ty| {
  29601             return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src);
  29602         }
  29603     }
  29604 
  29605     // Slices
  29606     if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) {
  29607         return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
  29608     }
  29609 
  29610     // Functions
  29611     if (dest_tag == .@"fn" and src_tag == .@"fn") {
  29612         return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
  29613     }
  29614 
  29615     // Error Unions
  29616     if (dest_tag == .error_union and src_tag == .error_union) {
  29617         const dest_payload = dest_ty.errorUnionPayload(zcu);
  29618         const src_payload = src_ty.errorUnionPayload(zcu);
  29619         const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src, null);
  29620         if (child != .ok) {
  29621             return .{ .error_union_payload = .{
  29622                 .child = try child.dupe(sema.arena),
  29623                 .actual = src_payload,
  29624                 .wanted = dest_payload,
  29625             } };
  29626         }
  29627         return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(zcu), src_ty.errorUnionSet(zcu), dest_is_mut, target, dest_src, src_src, null);
  29628     }
  29629 
  29630     // Error Sets
  29631     if (dest_tag == .error_set and src_tag == .error_set) {
  29632         const res1 = try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src);
  29633         if (!dest_is_mut or res1 != .ok) return res1;
  29634         // src -> dest is okay, but `dest_is_mut`, so it needs to be allowed in the other direction.
  29635         const res2 = try sema.coerceInMemoryAllowedErrorSets(block, src_ty, dest_ty, src_src, dest_src);
  29636         return res2;
  29637     }
  29638 
  29639     // Arrays
  29640     if (dest_tag == .array and src_tag == .array) {
  29641         const dest_info = dest_ty.arrayInfo(zcu);
  29642         const src_info = src_ty.arrayInfo(zcu);
  29643         if (dest_info.len != src_info.len) {
  29644             return .{ .array_len = .{
  29645                 .actual = src_info.len,
  29646                 .wanted = dest_info.len,
  29647             } };
  29648         }
  29649 
  29650         const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src, null);
  29651         switch (child) {
  29652             .ok => {},
  29653             .no_match => return child,
  29654             else => {
  29655                 return .{ .array_elem = .{
  29656                     .child = try child.dupe(sema.arena),
  29657                     .actual = src_info.elem_type,
  29658                     .wanted = dest_info.elem_type,
  29659                 } };
  29660             },
  29661         }
  29662         const ok_sent = (dest_info.sentinel == null and src_info.sentinel == null) or
  29663             (src_info.sentinel != null and
  29664                 dest_info.sentinel != null and
  29665                 dest_info.sentinel.?.eql(
  29666                     try pt.getCoerced(src_info.sentinel.?, dest_info.elem_type),
  29667                     dest_info.elem_type,
  29668                     zcu,
  29669                 ));
  29670         if (!ok_sent) {
  29671             return .{ .array_sentinel = .{
  29672                 .actual = src_info.sentinel orelse Value.@"unreachable",
  29673                 .wanted = dest_info.sentinel orelse Value.@"unreachable",
  29674                 .ty = dest_info.elem_type,
  29675             } };
  29676         }
  29677         return .ok;
  29678     }
  29679 
  29680     // Vectors
  29681     if (dest_tag == .vector and src_tag == .vector) {
  29682         const dest_len = dest_ty.vectorLen(zcu);
  29683         const src_len = src_ty.vectorLen(zcu);
  29684         if (dest_len != src_len) {
  29685             return .{ .vector_len = .{
  29686                 .actual = src_len,
  29687                 .wanted = dest_len,
  29688             } };
  29689         }
  29690 
  29691         const dest_elem_ty = dest_ty.scalarType(zcu);
  29692         const src_elem_ty = src_ty.scalarType(zcu);
  29693         const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null);
  29694         if (child != .ok) {
  29695             return .{ .vector_elem = .{
  29696                 .child = try child.dupe(sema.arena),
  29697                 .actual = src_elem_ty,
  29698                 .wanted = dest_elem_ty,
  29699             } };
  29700         }
  29701 
  29702         return .ok;
  29703     }
  29704 
  29705     // Optionals
  29706     if (dest_tag == .optional and src_tag == .optional) {
  29707         if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
  29708             return .{ .optional_shape = .{
  29709                 .actual = src_ty,
  29710                 .wanted = dest_ty,
  29711             } };
  29712         }
  29713         const dest_child_type = dest_ty.optionalChild(zcu);
  29714         const src_child_type = src_ty.optionalChild(zcu);
  29715 
  29716         const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src, null);
  29717         if (child != .ok) {
  29718             return .{ .optional_child = .{
  29719                 .child = try child.dupe(sema.arena),
  29720                 .actual = src_child_type,
  29721                 .wanted = dest_child_type,
  29722             } };
  29723         }
  29724 
  29725         return .ok;
  29726     }
  29727 
  29728     // Tuples (with in-memory-coercible fields)
  29729     if (dest_ty.isTuple(zcu) and src_ty.isTuple(zcu)) tuple: {
  29730         if (dest_ty.structFieldCount(zcu) != src_ty.structFieldCount(zcu)) break :tuple;
  29731         const field_count = dest_ty.structFieldCount(zcu);
  29732         for (0..field_count) |field_idx| {
  29733             if (dest_ty.structFieldIsComptime(field_idx, zcu) != src_ty.structFieldIsComptime(field_idx, zcu)) break :tuple;
  29734             if (dest_ty.fieldAlignment(field_idx, zcu) != src_ty.fieldAlignment(field_idx, zcu)) break :tuple;
  29735             const dest_field_ty = dest_ty.fieldType(field_idx, zcu);
  29736             const src_field_ty = src_ty.fieldType(field_idx, zcu);
  29737             const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src, null);
  29738             if (field != .ok) break :tuple;
  29739         }
  29740         return .ok;
  29741     }
  29742 
  29743     return .{ .no_match = .{
  29744         .actual = dest_ty,
  29745         .wanted = src_ty,
  29746     } };
  29747 }
  29748 
  29749 fn coerceInMemoryAllowedErrorSets(
  29750     sema: *Sema,
  29751     block: *Block,
  29752     dest_ty: Type,
  29753     src_ty: Type,
  29754     dest_src: LazySrcLoc,
  29755     src_src: LazySrcLoc,
  29756 ) !InMemoryCoercionResult {
  29757     const pt = sema.pt;
  29758     const zcu = pt.zcu;
  29759     const gpa = sema.gpa;
  29760     const ip = &zcu.intern_pool;
  29761 
  29762     // Coercion to `anyerror`. Note that this check can return false negatives
  29763     // in case the error sets did not get resolved.
  29764     if (dest_ty.isAnyError(zcu)) {
  29765         return .ok;
  29766     }
  29767 
  29768     if (dest_ty.toIntern() == .adhoc_inferred_error_set_type) {
  29769         // We are trying to coerce an error set to the current function's
  29770         // inferred error set.
  29771         const dst_ies = sema.fn_ret_ty_ies.?;
  29772         try dst_ies.addErrorSet(src_ty, ip, sema.arena);
  29773         return .ok;
  29774     }
  29775 
  29776     if (ip.isInferredErrorSetType(dest_ty.toIntern())) {
  29777         const dst_ies_func_index = ip.iesFuncIndex(dest_ty.toIntern());
  29778         if (sema.fn_ret_ty_ies) |dst_ies| {
  29779             if (dst_ies.func == dst_ies_func_index) {
  29780                 // We are trying to coerce an error set to the current function's
  29781                 // inferred error set.
  29782                 try dst_ies.addErrorSet(src_ty, ip, sema.arena);
  29783                 return .ok;
  29784             }
  29785         }
  29786         switch (try sema.resolveInferredErrorSet(block, dest_src, dest_ty.toIntern())) {
  29787             // isAnyError might have changed from a false negative to a true
  29788             // positive after resolution.
  29789             .anyerror_type => return .ok,
  29790             else => {},
  29791         }
  29792     }
  29793 
  29794     var missing_error_buf = std.array_list.Managed(InternPool.NullTerminatedString).init(gpa);
  29795     defer missing_error_buf.deinit();
  29796 
  29797     switch (src_ty.toIntern()) {
  29798         .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) {
  29799             .simple_type => unreachable, // filtered out above
  29800             .error_set_type, .inferred_error_set_type => return .from_anyerror,
  29801             else => unreachable,
  29802         },
  29803 
  29804         else => switch (ip.indexToKey(src_ty.toIntern())) {
  29805             .inferred_error_set_type => {
  29806                 const resolved_src_ty = try sema.resolveInferredErrorSet(block, src_src, src_ty.toIntern());
  29807                 // src anyerror status might have changed after the resolution.
  29808                 if (resolved_src_ty == .anyerror_type) {
  29809                     // dest_ty.isAnyError(zcu) == true is already checked for at this point.
  29810                     return .from_anyerror;
  29811                 }
  29812 
  29813                 for (ip.indexToKey(resolved_src_ty).error_set_type.names.get(ip)) |key| {
  29814                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) {
  29815                         try missing_error_buf.append(key);
  29816                     }
  29817                 }
  29818 
  29819                 if (missing_error_buf.items.len != 0) {
  29820                     return InMemoryCoercionResult{
  29821                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  29822                     };
  29823                 }
  29824 
  29825                 return .ok;
  29826             },
  29827             .error_set_type => |error_set_type| {
  29828                 for (error_set_type.names.get(ip)) |name| {
  29829                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) {
  29830                         try missing_error_buf.append(name);
  29831                     }
  29832                 }
  29833 
  29834                 if (missing_error_buf.items.len != 0) {
  29835                     return InMemoryCoercionResult{
  29836                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  29837                     };
  29838                 }
  29839 
  29840                 return .ok;
  29841             },
  29842             else => unreachable,
  29843         },
  29844     }
  29845 }
  29846 
  29847 fn coerceInMemoryAllowedFns(
  29848     sema: *Sema,
  29849     block: *Block,
  29850     dest_ty: Type,
  29851     src_ty: Type,
  29852     /// If set, the coercion must be valid in both directions.
  29853     dest_is_mut: bool,
  29854     target: *const std.Target,
  29855     dest_src: LazySrcLoc,
  29856     src_src: LazySrcLoc,
  29857 ) !InMemoryCoercionResult {
  29858     const pt = sema.pt;
  29859     const zcu = pt.zcu;
  29860     const ip = &zcu.intern_pool;
  29861 
  29862     const dest_info = zcu.typeToFunc(dest_ty).?;
  29863     const src_info = zcu.typeToFunc(src_ty).?;
  29864 
  29865     {
  29866         if (dest_info.is_var_args != src_info.is_var_args) {
  29867             return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args };
  29868         }
  29869 
  29870         if (dest_info.is_generic != src_info.is_generic) {
  29871             return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic };
  29872         }
  29873 
  29874         const callconv_ok = callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and
  29875             (!dest_is_mut or callconvCoerceAllowed(target, dest_info.cc, src_info.cc));
  29876 
  29877         if (!callconv_ok) {
  29878             return .{ .fn_cc = .{
  29879                 .actual = src_info.cc,
  29880                 .wanted = dest_info.cc,
  29881             } };
  29882         }
  29883 
  29884         if (!switch (src_info.return_type) {
  29885             .generic_poison_type => true,
  29886             .noreturn_type => !dest_is_mut,
  29887             else => false,
  29888         }) {
  29889             const rt = try sema.coerceInMemoryAllowed(
  29890                 block,
  29891                 .fromInterned(dest_info.return_type),
  29892                 .fromInterned(src_info.return_type),
  29893                 dest_is_mut,
  29894                 target,
  29895                 dest_src,
  29896                 src_src,
  29897                 null,
  29898             );
  29899             if (rt != .ok) return .{ .fn_return_type = .{
  29900                 .child = try rt.dupe(sema.arena),
  29901                 .actual = .fromInterned(src_info.return_type),
  29902                 .wanted = .fromInterned(dest_info.return_type),
  29903             } };
  29904         }
  29905     }
  29906 
  29907     const params_len = params_len: {
  29908         if (dest_info.param_types.len != src_info.param_types.len) {
  29909             return .{ .fn_param_count = .{
  29910                 .actual = src_info.param_types.len,
  29911                 .wanted = dest_info.param_types.len,
  29912             } };
  29913         }
  29914 
  29915         if (dest_info.noalias_bits != src_info.noalias_bits) {
  29916             return .{ .fn_param_noalias = .{
  29917                 .actual = src_info.noalias_bits,
  29918                 .wanted = dest_info.noalias_bits,
  29919             } };
  29920         }
  29921 
  29922         break :params_len dest_info.param_types.len;
  29923     };
  29924 
  29925     for (0..params_len) |param_i| {
  29926         const dest_param_ty: Type = .fromInterned(dest_info.param_types.get(ip)[param_i]);
  29927         const src_param_ty: Type = .fromInterned(src_info.param_types.get(ip)[param_i]);
  29928 
  29929         comptime_param: {
  29930             const src_is_comptime = src_info.paramIsComptime(@intCast(param_i));
  29931             const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i));
  29932             if (src_is_comptime == dest_is_comptime) break :comptime_param;
  29933             if (!dest_is_mut and src_is_comptime and !dest_is_comptime and try dest_param_ty.comptimeOnlySema(pt)) {
  29934                 // A parameter which is marked `comptime` can drop that annotation if the type is comptime-only.
  29935                 // The function remains generic, and the parameter is going to be comptime-resolved either way,
  29936                 // so this just affects whether or not the argument is comptime-evaluated at the call site.
  29937                 break :comptime_param;
  29938             }
  29939             return .{ .fn_param_comptime = .{
  29940                 .index = param_i,
  29941                 .wanted = dest_is_comptime,
  29942             } };
  29943         }
  29944 
  29945         if (!src_param_ty.isGenericPoison() and !dest_param_ty.isGenericPoison()) {
  29946             // Note: Cast direction is reversed here.
  29947             const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, dest_is_mut, target, dest_src, src_src, null);
  29948             if (param != .ok) {
  29949                 return .{ .fn_param = .{
  29950                     .child = try param.dupe(sema.arena),
  29951                     .actual = src_param_ty,
  29952                     .wanted = dest_param_ty,
  29953                     .index = param_i,
  29954                 } };
  29955             }
  29956         }
  29957     }
  29958 
  29959     return .ok;
  29960 }
  29961 
  29962 fn callconvCoerceAllowed(
  29963     target: *const std.Target,
  29964     src_cc: std.builtin.CallingConvention,
  29965     dest_cc: std.builtin.CallingConvention,
  29966 ) bool {
  29967     const Tag = std.builtin.CallingConvention.Tag;
  29968     if (@as(Tag, src_cc) != @as(Tag, dest_cc)) return false;
  29969 
  29970     switch (src_cc) {
  29971         inline else => |src_data, tag| {
  29972             const dest_data = @field(dest_cc, @tagName(tag));
  29973             if (@TypeOf(src_data) != void) {
  29974                 const default_stack_align = target.stackAlignment();
  29975                 const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align;
  29976                 const dest_stack_align = src_data.incoming_stack_alignment orelse default_stack_align;
  29977                 if (dest_stack_align < src_stack_align) return false;
  29978             }
  29979             switch (@TypeOf(src_data)) {
  29980                 void, std.builtin.CallingConvention.CommonOptions => {},
  29981                 std.builtin.CallingConvention.X86RegparmOptions => {
  29982                     if (src_data.register_params != dest_data.register_params) return false;
  29983                 },
  29984                 std.builtin.CallingConvention.ArmInterruptOptions => {
  29985                     if (src_data.type != dest_data.type) return false;
  29986                 },
  29987                 std.builtin.CallingConvention.MipsInterruptOptions => {
  29988                     if (src_data.mode != dest_data.mode) return false;
  29989                 },
  29990                 std.builtin.CallingConvention.RiscvInterruptOptions => {
  29991                     if (src_data.mode != dest_data.mode) return false;
  29992                 },
  29993                 else => comptime unreachable,
  29994             }
  29995         },
  29996     }
  29997     return true;
  29998 }
  29999 
  30000 fn coerceInMemoryAllowedPtrs(
  30001     sema: *Sema,
  30002     block: *Block,
  30003     dest_ty: Type,
  30004     src_ty: Type,
  30005     dest_ptr_ty: Type,
  30006     src_ptr_ty: Type,
  30007     /// If set, the coercion must be valid in both directions.
  30008     dest_is_mut: bool,
  30009     target: *const std.Target,
  30010     dest_src: LazySrcLoc,
  30011     src_src: LazySrcLoc,
  30012 ) !InMemoryCoercionResult {
  30013     const pt = sema.pt;
  30014     const zcu = pt.zcu;
  30015     const dest_info = dest_ptr_ty.ptrInfo(zcu);
  30016     const src_info = src_ptr_ty.ptrInfo(zcu);
  30017 
  30018     const ok_ptr_size = src_info.flags.size == dest_info.flags.size or
  30019         src_info.flags.size == .c or dest_info.flags.size == .c;
  30020     if (!ok_ptr_size) {
  30021         return InMemoryCoercionResult{ .ptr_size = .{
  30022             .actual = src_info.flags.size,
  30023             .wanted = dest_info.flags.size,
  30024         } };
  30025     }
  30026 
  30027     const ok_const = src_info.flags.is_const == dest_info.flags.is_const or
  30028         (!dest_is_mut and dest_info.flags.is_const);
  30029 
  30030     if (!ok_const) return .{ .ptr_const = .{
  30031         .actual = src_ty,
  30032         .wanted = dest_ty,
  30033     } };
  30034 
  30035     const ok_volatile = src_info.flags.is_volatile == dest_info.flags.is_volatile or
  30036         (!dest_is_mut and dest_info.flags.is_volatile);
  30037 
  30038     if (!ok_volatile) return .{ .ptr_volatile = .{
  30039         .actual = src_ty,
  30040         .wanted = dest_ty,
  30041     } };
  30042 
  30043     const dest_allowzero = dest_ty.ptrAllowsZero(zcu);
  30044     const src_allowzero = src_ty.ptrAllowsZero(zcu);
  30045     const ok_allowzero = src_allowzero == dest_allowzero or
  30046         (!dest_is_mut and dest_allowzero);
  30047 
  30048     if (!ok_allowzero) return .{ .ptr_allowzero = .{
  30049         .actual = src_ty,
  30050         .wanted = dest_ty,
  30051     } };
  30052 
  30053     if (dest_info.flags.address_space != src_info.flags.address_space) {
  30054         return .{ .ptr_addrspace = .{
  30055             .actual = src_info.flags.address_space,
  30056             .wanted = dest_info.flags.address_space,
  30057         } };
  30058     }
  30059 
  30060     const dest_child: Type = .fromInterned(dest_info.child);
  30061     const src_child: Type = .fromInterned(src_info.child);
  30062     const child = try sema.coerceInMemoryAllowed(
  30063         block,
  30064         dest_child,
  30065         src_child,
  30066         // We must also include `dest_is_mut`.
  30067         // Otherwise, this code is valid:
  30068         //
  30069         // const b: B = ...;
  30070         // var pa: *const A = undefined;
  30071         // const ppa: **const A = &pa;
  30072         // const ppb: **const B = ppa; // <-- this is what that allows
  30073         // ppb.* = &b;
  30074         // const a: A = pa.*;
  30075         //
  30076         // ...effectively performing an in-memory coercion from B to A.
  30077         dest_is_mut or !dest_info.flags.is_const,
  30078         target,
  30079         dest_src,
  30080         src_src,
  30081         null,
  30082     );
  30083     if (child != .ok and !dest_is_mut) allow: {
  30084         // As a special case, we also allow coercing `*[n:s]T` to `*[n]T`, akin to dropping the sentinel from a slice.
  30085         // `*[n:s]T` cannot coerce in memory to `*[n]T` since they have different sizes.
  30086         if (src_child.zigTypeTag(zcu) == .array and dest_child.zigTypeTag(zcu) == .array and
  30087             src_child.arrayLen(zcu) == dest_child.arrayLen(zcu) and
  30088             src_child.sentinel(zcu) != null and dest_child.sentinel(zcu) == null and
  30089             .ok == try sema.coerceInMemoryAllowed(block, dest_child.childType(zcu), src_child.childType(zcu), !dest_info.flags.is_const, target, dest_src, src_src, null))
  30090         {
  30091             break :allow;
  30092         }
  30093         return .{ .ptr_child = .{
  30094             .child = try child.dupe(sema.arena),
  30095             .actual = .fromInterned(src_info.child),
  30096             .wanted = .fromInterned(dest_info.child),
  30097         } };
  30098     }
  30099 
  30100     if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or
  30101         src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset)
  30102     {
  30103         return .{ .ptr_bit_range = .{
  30104             .actual_host = src_info.packed_offset.host_size,
  30105             .wanted_host = dest_info.packed_offset.host_size,
  30106             .actual_offset = src_info.packed_offset.bit_offset,
  30107             .wanted_offset = dest_info.packed_offset.bit_offset,
  30108         } };
  30109     }
  30110 
  30111     const sentinel_ok = ok: {
  30112         const ss = src_info.sentinel;
  30113         const ds = dest_info.sentinel;
  30114         if (ss == .none and ds == .none) break :ok true;
  30115         if (ss != .none and ds != .none) {
  30116             if (ds == try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, ss, dest_info.child)) break :ok true;
  30117         }
  30118         if (src_info.flags.size == .c) break :ok true;
  30119         if (!dest_is_mut and dest_info.sentinel == .none) break :ok true;
  30120         break :ok false;
  30121     };
  30122 
  30123     if (!sentinel_ok) {
  30124         return .{ .ptr_sentinel = .{
  30125             .actual = switch (src_info.sentinel) {
  30126                 .none => Value.@"unreachable",
  30127                 else => Value.fromInterned(src_info.sentinel),
  30128             },
  30129             .wanted = switch (dest_info.sentinel) {
  30130                 .none => Value.@"unreachable",
  30131                 else => Value.fromInterned(dest_info.sentinel),
  30132             },
  30133             .ty = .fromInterned(dest_info.child),
  30134         } };
  30135     }
  30136 
  30137     // If both pointers have alignment 0, it means they both want ABI alignment.
  30138     // In this case, if they share the same child type, no need to resolve
  30139     // pointee type alignment. Otherwise both pointee types must have their alignment
  30140     // resolved and we compare the alignment numerically.
  30141     if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or
  30142         dest_info.child != src_info.child)
  30143     {
  30144         const src_align = if (src_info.flags.alignment != .none)
  30145             src_info.flags.alignment
  30146         else
  30147             try Type.fromInterned(src_info.child).abiAlignmentSema(pt);
  30148 
  30149         const dest_align = if (dest_info.flags.alignment != .none)
  30150             dest_info.flags.alignment
  30151         else
  30152             try Type.fromInterned(dest_info.child).abiAlignmentSema(pt);
  30153 
  30154         if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) {
  30155             return InMemoryCoercionResult{ .ptr_alignment = .{
  30156                 .actual = src_align,
  30157                 .wanted = dest_align,
  30158             } };
  30159         }
  30160     }
  30161 
  30162     return .ok;
  30163 }
  30164 
  30165 fn coerceVarArgParam(
  30166     sema: *Sema,
  30167     block: *Block,
  30168     inst: Air.Inst.Ref,
  30169     inst_src: LazySrcLoc,
  30170 ) !Air.Inst.Ref {
  30171     if (block.is_typeof) return inst;
  30172 
  30173     const pt = sema.pt;
  30174     const zcu = pt.zcu;
  30175     const uncasted_ty = sema.typeOf(inst);
  30176     const coerced = switch (uncasted_ty.zigTypeTag(zcu)) {
  30177         // TODO consider casting to c_int/f64 if they fit
  30178         .comptime_int, .comptime_float => return sema.fail(
  30179             block,
  30180             inst_src,
  30181             "integer and float literals passed to variadic function must be casted to a fixed-size number type",
  30182             .{},
  30183         ),
  30184         .@"fn" => fn_ptr: {
  30185             const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
  30186             const fn_nav = zcu.funcInfo(fn_val.toIntern()).owner_nav;
  30187             break :fn_ptr try sema.analyzeNavRef(block, inst_src, fn_nav);
  30188         },
  30189         .array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
  30190         .float => float: {
  30191             const target = zcu.getTarget();
  30192             const double_bits = target.cTypeBitSize(.double);
  30193             const inst_bits = uncasted_ty.floatBits(target);
  30194             if (inst_bits >= double_bits) break :float inst;
  30195             switch (double_bits) {
  30196                 32 => break :float try sema.coerce(block, .f32, inst, inst_src),
  30197                 64 => break :float try sema.coerce(block, .f64, inst, inst_src),
  30198                 else => unreachable,
  30199             }
  30200         },
  30201         else => if (uncasted_ty.isAbiInt(zcu)) int: {
  30202             if (!try sema.validateExternType(uncasted_ty, .param_ty)) break :int inst;
  30203             const target = zcu.getTarget();
  30204             const uncasted_info = uncasted_ty.intInfo(zcu);
  30205             if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) {
  30206                 .signed => .int,
  30207                 .unsigned => .uint,
  30208             })) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
  30209                 .signed => .c_int,
  30210                 .unsigned => .c_uint,
  30211             }, inst, inst_src);
  30212             if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) {
  30213                 .signed => .long,
  30214                 .unsigned => .ulong,
  30215             })) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
  30216                 .signed => .c_long,
  30217                 .unsigned => .c_ulong,
  30218             }, inst, inst_src);
  30219             if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) {
  30220                 .signed => .longlong,
  30221                 .unsigned => .ulonglong,
  30222             })) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
  30223                 .signed => .c_longlong,
  30224                 .unsigned => .c_ulonglong,
  30225             }, inst, inst_src);
  30226             break :int inst;
  30227         } else inst,
  30228     };
  30229 
  30230     const coerced_ty = sema.typeOf(coerced);
  30231     if (!try sema.validateExternType(coerced_ty, .param_ty)) {
  30232         const msg = msg: {
  30233             const msg = try sema.errMsg(inst_src, "cannot pass '{f}' to variadic function", .{coerced_ty.fmt(pt)});
  30234             errdefer msg.destroy(sema.gpa);
  30235 
  30236             try sema.explainWhyTypeIsNotExtern(msg, inst_src, coerced_ty, .param_ty);
  30237 
  30238             try sema.addDeclaredHereNote(msg, coerced_ty);
  30239             break :msg msg;
  30240         };
  30241         return sema.failWithOwnedErrorMsg(block, msg);
  30242     }
  30243     return coerced;
  30244 }
  30245 
  30246 // TODO migrate callsites to use storePtr2 instead.
  30247 fn storePtr(
  30248     sema: *Sema,
  30249     block: *Block,
  30250     src: LazySrcLoc,
  30251     ptr: Air.Inst.Ref,
  30252     uncasted_operand: Air.Inst.Ref,
  30253 ) CompileError!void {
  30254     const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store;
  30255     return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag);
  30256 }
  30257 
  30258 fn storePtr2(
  30259     sema: *Sema,
  30260     block: *Block,
  30261     src: LazySrcLoc,
  30262     ptr: Air.Inst.Ref,
  30263     ptr_src: LazySrcLoc,
  30264     uncasted_operand: Air.Inst.Ref,
  30265     operand_src: LazySrcLoc,
  30266     air_tag: Air.Inst.Tag,
  30267 ) CompileError!void {
  30268     const pt = sema.pt;
  30269     const zcu = pt.zcu;
  30270     const ptr_ty = sema.typeOf(ptr);
  30271     if (ptr_ty.isConstPtr(zcu))
  30272         return sema.fail(block, ptr_src, "cannot assign to constant", .{});
  30273 
  30274     const elem_ty = ptr_ty.childType(zcu);
  30275 
  30276     // To generate better code for tuples, we detect a tuple operand here, and
  30277     // analyze field loads and stores directly. This avoids an extra allocation + memcpy
  30278     // which would occur if we used `coerce`.
  30279     // However, we avoid this mechanism if the destination element type is a tuple,
  30280     // because the regular store will be better for this case.
  30281     // If the destination type is a struct we don't want this mechanism to trigger, because
  30282     // this code does not handle tuple-to-struct coercion which requires dealing with missing
  30283     // fields.
  30284     const operand_ty = sema.typeOf(uncasted_operand);
  30285     if (operand_ty.isTuple(zcu) and elem_ty.zigTypeTag(zcu) == .array) {
  30286         const field_count = operand_ty.structFieldCount(zcu);
  30287         var i: u32 = 0;
  30288         while (i < field_count) : (i += 1) {
  30289             const elem_src = operand_src; // TODO better source location
  30290             const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);
  30291             const elem_index = try pt.intRef(.usize, i);
  30292             const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true);
  30293             try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
  30294         }
  30295         return;
  30296     }
  30297 
  30298     // TODO do the same thing for anon structs as for tuples above.
  30299     // However, beware of the need to handle missing/extra fields.
  30300 
  30301     const is_ret = air_tag == .ret_ptr;
  30302 
  30303     // Detect if we are storing an array operand to a bitcasted vector pointer.
  30304     // If so, we instead reach through the bitcasted pointer to the vector pointer,
  30305     // bitcast the array operand to a vector, and then lower this as a store of
  30306     // a vector value to a vector pointer. This generally results in better code,
  30307     // as well as working around an LLVM bug:
  30308     // https://github.com/ziglang/zig/issues/11154
  30309     if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
  30310         const vector_ty = sema.typeOf(vector_ptr).childType(zcu);
  30311         const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  30312             error.NotCoercible => unreachable,
  30313             else => |e| return e,
  30314         };
  30315         try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store);
  30316         return;
  30317     }
  30318 
  30319     const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  30320         error.NotCoercible => unreachable,
  30321         else => |e| return e,
  30322     };
  30323     const maybe_operand_val = try sema.resolveValue(operand);
  30324 
  30325     const runtime_src = rs: {
  30326         const ptr_val = try sema.resolveDefinedValue(block, ptr_src, ptr) orelse break :rs ptr_src;
  30327         if (!sema.isComptimeMutablePtr(ptr_val)) break :rs ptr_src;
  30328         const operand_val = maybe_operand_val orelse return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{});
  30329         return sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
  30330     };
  30331 
  30332     // We're performing the store at runtime; as such, we need to make sure the pointee type
  30333     // is not comptime-only. We can hit this case with a `@ptrFromInt` pointer.
  30334     if (try elem_ty.comptimeOnlySema(pt)) {
  30335         return sema.failWithOwnedErrorMsg(block, msg: {
  30336             const msg = try sema.errMsg(src, "cannot store comptime-only type '{f}' at runtime", .{elem_ty.fmt(pt)});
  30337             errdefer msg.destroy(sema.gpa);
  30338             try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{});
  30339             break :msg msg;
  30340         });
  30341     }
  30342 
  30343     // We do this after the possible comptime store above, for the case of field_ptr stores
  30344     // to unions because we want the comptime tag to be set, even if the field type is void.
  30345     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
  30346         return;
  30347     }
  30348 
  30349     try sema.requireRuntimeBlock(block, src, runtime_src);
  30350 
  30351     if (ptr_ty.ptrInfo(zcu).flags.vector_index == .runtime) {
  30352         const ptr_inst = ptr.toIndex().?;
  30353         const air_tags = sema.air_instructions.items(.tag);
  30354         if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) {
  30355             const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl;
  30356             const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
  30357             _ = try block.addInst(.{
  30358                 .tag = .vector_store_elem,
  30359                 .data = .{ .vector_store_elem = .{
  30360                     .vector_ptr = bin_op.lhs,
  30361                     .payload = try block.sema.addExtra(Air.Bin{
  30362                         .lhs = bin_op.rhs,
  30363                         .rhs = operand,
  30364                     }),
  30365                 } },
  30366             });
  30367             return;
  30368         }
  30369         return sema.fail(block, ptr_src, "unable to determine vector element index of type '{f}'", .{
  30370             ptr_ty.fmt(pt),
  30371         });
  30372     }
  30373 
  30374     const store_inst = if (is_ret)
  30375         try block.addBinOp(.store, ptr, operand)
  30376     else
  30377         try block.addBinOp(air_tag, ptr, operand);
  30378 
  30379     try sema.checkComptimeKnownStore(block, store_inst, operand_src);
  30380 
  30381     return;
  30382 }
  30383 
  30384 /// Given an AIR store instruction, checks whether we are performing a
  30385 /// comptime-known store to a local alloc, and updates `maybe_comptime_allocs`
  30386 /// accordingly.
  30387 /// Handles calling `validateRuntimeValue` if the store is runtime for any reason.
  30388 fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst.Ref, store_src: LazySrcLoc) !void {
  30389     const store_inst = store_inst_ref.toIndex().?;
  30390     const inst_data = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
  30391     const ptr = inst_data.lhs.toIndex() orelse return;
  30392     const operand = inst_data.rhs;
  30393 
  30394     known: {
  30395         const maybe_base_alloc = sema.base_allocs.get(ptr) orelse break :known;
  30396         const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse break :known;
  30397 
  30398         if ((try sema.resolveValue(operand)) != null and
  30399             block.runtime_index == maybe_comptime_alloc.runtime_index)
  30400         {
  30401             try maybe_comptime_alloc.stores.append(sema.arena, .{
  30402                 .inst = store_inst,
  30403                 .src = store_src,
  30404             });
  30405             return;
  30406         }
  30407 
  30408         // We're newly discovering that this alloc is runtime-known.
  30409         try sema.markMaybeComptimeAllocRuntime(block, maybe_base_alloc);
  30410     }
  30411 
  30412     try sema.validateRuntimeValue(block, store_src, operand);
  30413 }
  30414 
  30415 /// Given an AIR instruction transforming a pointer (struct_field_ptr,
  30416 /// ptr_elem_ptr, bitcast, etc), checks whether the base pointer refers to a
  30417 /// local alloc, and updates `base_allocs` accordingly.
  30418 fn checkKnownAllocPtr(sema: *Sema, block: *Block, base_ptr: Air.Inst.Ref, new_ptr: Air.Inst.Ref) !void {
  30419     const base_ptr_inst = base_ptr.toIndex() orelse return;
  30420     const new_ptr_inst = new_ptr.toIndex() orelse return;
  30421     const alloc_inst = sema.base_allocs.get(base_ptr_inst) orelse return;
  30422     try sema.base_allocs.put(sema.gpa, new_ptr_inst, alloc_inst);
  30423 
  30424     switch (sema.air_instructions.items(.tag)[@intFromEnum(new_ptr_inst)]) {
  30425         .optional_payload_ptr_set, .errunion_payload_ptr_set => {
  30426             const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(alloc_inst) orelse return;
  30427 
  30428             // This is functionally a store, since it writes the optional payload bit.
  30429             // Thus, if it is behind a runtime condition, we must mark the alloc as runtime appropriately.
  30430             if (block.runtime_index != maybe_comptime_alloc.runtime_index) {
  30431                 return sema.markMaybeComptimeAllocRuntime(block, alloc_inst);
  30432             }
  30433 
  30434             try maybe_comptime_alloc.stores.append(sema.arena, .{
  30435                 .inst = new_ptr_inst,
  30436                 .src = LazySrcLoc.unneeded,
  30437             });
  30438         },
  30439         .ptr_elem_ptr => {
  30440             const tmp_air = sema.getTmpAir();
  30441             const pl_idx = tmp_air.instructions.items(.data)[@intFromEnum(new_ptr_inst)].ty_pl.payload;
  30442             const bin = tmp_air.extraData(Air.Bin, pl_idx).data;
  30443             const index_ref = bin.rhs;
  30444 
  30445             // If the index value is runtime-known, this pointer is also runtime-known, so
  30446             // we must in turn make the alloc value runtime-known.
  30447             if (null == try sema.resolveValue(index_ref)) {
  30448                 try sema.markMaybeComptimeAllocRuntime(block, alloc_inst);
  30449             }
  30450         },
  30451         else => {},
  30452     }
  30453 }
  30454 
  30455 fn markMaybeComptimeAllocRuntime(sema: *Sema, block: *Block, alloc_inst: Air.Inst.Index) CompileError!void {
  30456     const maybe_comptime_alloc = (sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return).value;
  30457     // Since the alloc has been determined to be runtime, we must check that
  30458     // all other stores to it are permitted to be runtime values.
  30459     const slice = maybe_comptime_alloc.stores.slice();
  30460     for (slice.items(.inst), slice.items(.src)) |other_inst, other_src| {
  30461         if (other_src.offset == .unneeded) {
  30462             switch (sema.air_instructions.items(.tag)[@intFromEnum(other_inst)]) {
  30463                 .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => continue,
  30464                 else => unreachable, // assertion failure
  30465             }
  30466         }
  30467         const other_data = sema.air_instructions.items(.data)[@intFromEnum(other_inst)].bin_op;
  30468         const other_operand = other_data.rhs;
  30469         try sema.validateRuntimeValue(block, other_src, other_operand);
  30470     }
  30471 }
  30472 
  30473 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector
  30474 /// pointer. Only if the final element type matches the vector element type, and the
  30475 /// lengths match.
  30476 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref {
  30477     const pt = sema.pt;
  30478     const zcu = pt.zcu;
  30479     const array_ty = sema.typeOf(ptr).childType(zcu);
  30480     if (array_ty.zigTypeTag(zcu) != .array) return null;
  30481     var ptr_ref = ptr;
  30482     var ptr_inst = ptr_ref.toIndex() orelse return null;
  30483     const air_datas = sema.air_instructions.items(.data);
  30484     const air_tags = sema.air_instructions.items(.tag);
  30485     const vector_ty = while (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
  30486         ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
  30487         if (!sema.isKnownZigType(ptr_ref, .pointer)) return null;
  30488         const child_ty = sema.typeOf(ptr_ref).childType(zcu);
  30489         if (child_ty.zigTypeTag(zcu) == .vector) break child_ty;
  30490         ptr_inst = ptr_ref.toIndex() orelse return null;
  30491     } else return null;
  30492 
  30493     // We have a pointer-to-array and a pointer-to-vector. If the elements and
  30494     // lengths match, return the result.
  30495     if (array_ty.childType(zcu).eql(vector_ty.childType(zcu), zcu) and
  30496         array_ty.arrayLen(zcu) == vector_ty.vectorLen(zcu))
  30497     {
  30498         return ptr_ref;
  30499     } else {
  30500         return null;
  30501     }
  30502 }
  30503 
  30504 /// Call when you have Value objects rather than Air instructions, and you want to
  30505 /// assert the store must be done at comptime.
  30506 fn storePtrVal(
  30507     sema: *Sema,
  30508     block: *Block,
  30509     src: LazySrcLoc,
  30510     ptr_val: Value,
  30511     operand_val: Value,
  30512     operand_ty: Type,
  30513 ) !void {
  30514     const pt = sema.pt;
  30515     const zcu = pt.zcu;
  30516     const ip = &zcu.intern_pool;
  30517     // TODO: audit use sites to eliminate this coercion
  30518     const coerced_operand_val = try pt.getCoerced(operand_val, operand_ty);
  30519     // TODO: audit use sites to eliminate this coercion
  30520     const ptr_ty = try pt.ptrType(info: {
  30521         var info = ptr_val.typeOf(zcu).ptrInfo(zcu);
  30522         info.child = operand_ty.toIntern();
  30523         break :info info;
  30524     });
  30525     const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty);
  30526 
  30527     switch (try sema.storeComptimePtr(block, src, coerced_ptr_val, coerced_operand_val)) {
  30528         .success => {},
  30529         .runtime_store => unreachable, // use sites check this
  30530         // TODO use failWithInvalidComptimeFieldStore
  30531         .comptime_field_mismatch => return sema.fail(
  30532             block,
  30533             src,
  30534             "value stored in comptime field does not match the default value of the field",
  30535             .{},
  30536         ),
  30537         .undef => return sema.failWithUseOfUndef(block, src, null),
  30538         .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}),
  30539         .null_payload => return sema.fail(block, src, "attempt to use null value", .{}),
  30540         .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}),
  30541         .needed_well_defined => |ty| return sema.fail(
  30542             block,
  30543             src,
  30544             "comptime dereference requires '{f}' to have a well-defined layout",
  30545             .{ty.fmt(pt)},
  30546         ),
  30547         .out_of_bounds => |ty| return sema.fail(
  30548             block,
  30549             src,
  30550             "dereference of '{f}' exceeds bounds of containing decl of type '{f}'",
  30551             .{ ptr_ty.fmt(pt), ty.fmt(pt) },
  30552         ),
  30553         .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}),
  30554     }
  30555 }
  30556 
  30557 fn bitCast(
  30558     sema: *Sema,
  30559     block: *Block,
  30560     dest_ty: Type,
  30561     inst: Air.Inst.Ref,
  30562     inst_src: LazySrcLoc,
  30563     operand_src: ?LazySrcLoc,
  30564 ) CompileError!Air.Inst.Ref {
  30565     const pt = sema.pt;
  30566     const zcu = pt.zcu;
  30567     try dest_ty.resolveLayout(pt);
  30568 
  30569     const old_ty = sema.typeOf(inst);
  30570     try old_ty.resolveLayout(pt);
  30571 
  30572     const dest_bits = dest_ty.bitSize(zcu);
  30573     const old_bits = old_ty.bitSize(zcu);
  30574 
  30575     if (old_bits != dest_bits) {
  30576         return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{
  30577             dest_ty.fmt(pt),
  30578             dest_bits,
  30579             old_ty.fmt(pt),
  30580             old_bits,
  30581         });
  30582     }
  30583 
  30584     if (try sema.resolveValue(inst)) |val| {
  30585         if (val.isUndef(zcu))
  30586             return pt.undefRef(dest_ty);
  30587         if (old_ty.zigTypeTag(zcu) == .error_set and dest_ty.zigTypeTag(zcu) == .error_set) {
  30588             // Special case: we sometimes call `bitCast` on error set values, but they
  30589             // don't have a well-defined layout, so we can't use `bitCastVal` on them.
  30590             return Air.internedToRef((try pt.getCoerced(val, dest_ty)).toIntern());
  30591         }
  30592         if (try sema.bitCastVal(val, dest_ty, 0, 0, 0)) |result_val| {
  30593             return Air.internedToRef(result_val.toIntern());
  30594         }
  30595     }
  30596     try sema.requireRuntimeBlock(block, inst_src, operand_src);
  30597     try sema.validateRuntimeValue(block, inst_src, inst);
  30598     return block.addBitCast(dest_ty, inst);
  30599 }
  30600 
  30601 fn coerceArrayPtrToSlice(
  30602     sema: *Sema,
  30603     block: *Block,
  30604     dest_ty: Type,
  30605     inst: Air.Inst.Ref,
  30606     inst_src: LazySrcLoc,
  30607 ) CompileError!Air.Inst.Ref {
  30608     const pt = sema.pt;
  30609     const zcu = pt.zcu;
  30610     if (try sema.resolveValue(inst)) |val| {
  30611         const ptr_array_ty = sema.typeOf(inst);
  30612         const array_ty = ptr_array_ty.childType(zcu);
  30613         const slice_ptr_ty = dest_ty.slicePtrFieldType(zcu);
  30614         const slice_ptr = try pt.getCoerced(val, slice_ptr_ty);
  30615         const slice_val = try pt.intern(.{ .slice = .{
  30616             .ty = dest_ty.toIntern(),
  30617             .ptr = slice_ptr.toIntern(),
  30618             .len = (try pt.intValue(.usize, array_ty.arrayLen(zcu))).toIntern(),
  30619         } });
  30620         return Air.internedToRef(slice_val);
  30621     }
  30622     try sema.requireRuntimeBlock(block, inst_src, null);
  30623     return block.addTyOp(.array_to_slice, dest_ty, inst);
  30624 }
  30625 
  30626 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool {
  30627     const pt = sema.pt;
  30628     const zcu = pt.zcu;
  30629     const dest_info = dest_ty.ptrInfo(zcu);
  30630     const inst_info = inst_ty.ptrInfo(zcu);
  30631     const len0 = (Type.fromInterned(inst_info.child).zigTypeTag(zcu) == .array and (Type.fromInterned(inst_info.child).arrayLenIncludingSentinel(zcu) == 0 or
  30632         (Type.fromInterned(inst_info.child).arrayLen(zcu) == 0 and dest_info.sentinel == .none and dest_info.flags.size != .c and dest_info.flags.size != .many))) or
  30633         (Type.fromInterned(inst_info.child).isTuple(zcu) and Type.fromInterned(inst_info.child).structFieldCount(zcu) == 0);
  30634 
  30635     const ok_const = (!inst_info.flags.is_const or dest_info.flags.is_const) or len0;
  30636     const ok_volatile = !inst_info.flags.is_volatile or dest_info.flags.is_volatile;
  30637     if (!ok_const) {
  30638         in_memory_result.* = .{ .ptr_const = .{
  30639             .actual = inst_ty,
  30640             .wanted = dest_ty,
  30641         } };
  30642         return false;
  30643     }
  30644     if (!ok_volatile) {
  30645         in_memory_result.* = .{ .ptr_volatile = .{
  30646             .actual = inst_ty,
  30647             .wanted = dest_ty,
  30648         } };
  30649         return false;
  30650     }
  30651 
  30652     if (dest_info.flags.address_space != inst_info.flags.address_space) {
  30653         in_memory_result.* = .{ .ptr_addrspace = .{
  30654             .actual = inst_info.flags.address_space,
  30655             .wanted = dest_info.flags.address_space,
  30656         } };
  30657         return false;
  30658     }
  30659     if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true;
  30660     if (len0) return true;
  30661 
  30662     const inst_align = if (inst_info.flags.alignment != .none)
  30663         inst_info.flags.alignment
  30664     else
  30665         Type.fromInterned(inst_info.child).abiAlignment(zcu);
  30666 
  30667     const dest_align = if (dest_info.flags.alignment != .none)
  30668         dest_info.flags.alignment
  30669     else
  30670         Type.fromInterned(dest_info.child).abiAlignment(zcu);
  30671 
  30672     if (dest_align.compare(.gt, inst_align)) {
  30673         in_memory_result.* = .{ .ptr_alignment = .{
  30674             .actual = inst_align,
  30675             .wanted = dest_align,
  30676         } };
  30677         return false;
  30678     }
  30679     return true;
  30680 }
  30681 
  30682 fn coerceCompatiblePtrs(
  30683     sema: *Sema,
  30684     block: *Block,
  30685     dest_ty: Type,
  30686     inst: Air.Inst.Ref,
  30687     inst_src: LazySrcLoc,
  30688 ) !Air.Inst.Ref {
  30689     const pt = sema.pt;
  30690     const zcu = pt.zcu;
  30691     const inst_ty = sema.typeOf(inst);
  30692     if (try sema.resolveValue(inst)) |val| {
  30693         if (!val.isUndef(zcu) and val.isNull(zcu) and !dest_ty.isAllowzeroPtr(zcu)) {
  30694             return sema.fail(block, inst_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)});
  30695         }
  30696         // The comptime Value representation is compatible with both types.
  30697         return Air.internedToRef(
  30698             (try pt.getCoerced(val, dest_ty)).toIntern(),
  30699         );
  30700     }
  30701     try sema.requireRuntimeBlock(block, inst_src, null);
  30702     const inst_allows_zero = inst_ty.zigTypeTag(zcu) != .pointer or inst_ty.ptrAllowsZero(zcu);
  30703     if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(zcu) and
  30704         (try dest_ty.elemType2(zcu).hasRuntimeBitsSema(pt) or dest_ty.elemType2(zcu).zigTypeTag(zcu) == .@"fn"))
  30705     {
  30706         try sema.checkLogicalPtrOperation(block, inst_src, inst_ty);
  30707         const actual_ptr = if (inst_ty.isSlice(zcu))
  30708             try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
  30709         else
  30710             inst;
  30711         const ptr_int = try block.addBitCast(.usize, actual_ptr);
  30712         const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
  30713         const ok = if (inst_ty.isSlice(zcu)) ok: {
  30714             const len = try sema.analyzeSliceLen(block, inst_src, inst);
  30715             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  30716             break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero);
  30717         } else is_non_zero;
  30718         try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null);
  30719     }
  30720     const new_ptr = try sema.bitCast(block, dest_ty, inst, inst_src, null);
  30721     try sema.checkKnownAllocPtr(block, inst, new_ptr);
  30722     return new_ptr;
  30723 }
  30724 
  30725 fn coerceEnumToUnion(
  30726     sema: *Sema,
  30727     block: *Block,
  30728     union_ty: Type,
  30729     union_ty_src: LazySrcLoc,
  30730     inst: Air.Inst.Ref,
  30731     inst_src: LazySrcLoc,
  30732 ) !Air.Inst.Ref {
  30733     const pt = sema.pt;
  30734     const zcu = pt.zcu;
  30735     const ip = &zcu.intern_pool;
  30736     const inst_ty = sema.typeOf(inst);
  30737 
  30738     const tag_ty = union_ty.unionTagType(zcu) orelse {
  30739         const msg = msg: {
  30740             const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{
  30741                 union_ty.fmt(pt), inst_ty.fmt(pt),
  30742             });
  30743             errdefer msg.destroy(sema.gpa);
  30744             try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{});
  30745             try sema.addDeclaredHereNote(msg, union_ty);
  30746             break :msg msg;
  30747         };
  30748         return sema.failWithOwnedErrorMsg(block, msg);
  30749     };
  30750 
  30751     const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
  30752     if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
  30753         const field_index = union_ty.unionTagFieldIndex(val, pt.zcu) orelse {
  30754             return sema.fail(block, inst_src, "union '{f}' has no tag with value '{f}'", .{
  30755                 union_ty.fmt(pt), val.fmtValueSema(pt, sema),
  30756             });
  30757         };
  30758 
  30759         const union_obj = zcu.typeToUnion(union_ty).?;
  30760         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  30761         try field_ty.resolveFields(pt);
  30762         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  30763             const msg = msg: {
  30764                 const msg = try sema.errMsg(inst_src, "cannot initialize 'noreturn' field of union", .{});
  30765                 errdefer msg.destroy(sema.gpa);
  30766 
  30767                 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index];
  30768                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
  30769                     field_name.fmt(ip),
  30770                 });
  30771                 try sema.addDeclaredHereNote(msg, union_ty);
  30772                 break :msg msg;
  30773             };
  30774             return sema.failWithOwnedErrorMsg(block, msg);
  30775         }
  30776         const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse {
  30777             const msg = msg: {
  30778                 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index];
  30779                 const msg = try sema.errMsg(inst_src, "coercion from enum '{f}' to union '{f}' must initialize '{f}' field '{f}'", .{
  30780                     inst_ty.fmt(pt),  union_ty.fmt(pt),
  30781                     field_ty.fmt(pt), field_name.fmt(ip),
  30782                 });
  30783                 errdefer msg.destroy(sema.gpa);
  30784 
  30785                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
  30786                     field_name.fmt(ip),
  30787                 });
  30788                 try sema.addDeclaredHereNote(msg, union_ty);
  30789                 break :msg msg;
  30790             };
  30791             return sema.failWithOwnedErrorMsg(block, msg);
  30792         };
  30793 
  30794         return Air.internedToRef((try pt.unionValue(union_ty, val, opv)).toIntern());
  30795     }
  30796 
  30797     try sema.requireRuntimeBlock(block, inst_src, null);
  30798 
  30799     if (tag_ty.isNonexhaustiveEnum(zcu)) {
  30800         const msg = msg: {
  30801             const msg = try sema.errMsg(inst_src, "runtime coercion to union '{f}' from non-exhaustive enum", .{
  30802                 union_ty.fmt(pt),
  30803             });
  30804             errdefer msg.destroy(sema.gpa);
  30805             try sema.addDeclaredHereNote(msg, tag_ty);
  30806             break :msg msg;
  30807         };
  30808         return sema.failWithOwnedErrorMsg(block, msg);
  30809     }
  30810 
  30811     const union_obj = zcu.typeToUnion(union_ty).?;
  30812     {
  30813         var msg: ?*Zcu.ErrorMsg = null;
  30814         errdefer if (msg) |some| some.destroy(sema.gpa);
  30815 
  30816         for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| {
  30817             if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .noreturn) {
  30818                 const err_msg = msg orelse try sema.errMsg(
  30819                     inst_src,
  30820                     "runtime coercion from enum '{f}' to union '{f}' which has a 'noreturn' field",
  30821                     .{ tag_ty.fmt(pt), union_ty.fmt(pt) },
  30822                 );
  30823                 msg = err_msg;
  30824 
  30825                 try sema.addFieldErrNote(union_ty, field_index, err_msg, "'noreturn' field here", .{});
  30826             }
  30827         }
  30828         if (msg) |some| {
  30829             msg = null;
  30830             try sema.addDeclaredHereNote(some, union_ty);
  30831             return sema.failWithOwnedErrorMsg(block, some);
  30832         }
  30833     }
  30834 
  30835     // If the union has all fields 0 bits, the union value is just the enum value.
  30836     if (union_ty.unionHasAllZeroBitFieldTypes(zcu)) {
  30837         return block.addBitCast(union_ty, enum_tag);
  30838     }
  30839 
  30840     const msg = msg: {
  30841         const msg = try sema.errMsg(
  30842             inst_src,
  30843             "runtime coercion from enum '{f}' to union '{f}' which has non-void fields",
  30844             .{ tag_ty.fmt(pt), union_ty.fmt(pt) },
  30845         );
  30846         errdefer msg.destroy(sema.gpa);
  30847 
  30848         for (0..union_obj.field_types.len) |field_index| {
  30849             const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index];
  30850             const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  30851             if (!(try field_ty.hasRuntimeBitsSema(pt))) continue;
  30852             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' has type '{f}'", .{
  30853                 field_name.fmt(ip),
  30854                 field_ty.fmt(pt),
  30855             });
  30856         }
  30857         try sema.addDeclaredHereNote(msg, union_ty);
  30858         break :msg msg;
  30859     };
  30860     return sema.failWithOwnedErrorMsg(block, msg);
  30861 }
  30862 
  30863 /// If the lengths match, coerces element-wise.
  30864 fn coerceArrayLike(
  30865     sema: *Sema,
  30866     block: *Block,
  30867     dest_ty: Type,
  30868     dest_ty_src: LazySrcLoc,
  30869     inst: Air.Inst.Ref,
  30870     inst_src: LazySrcLoc,
  30871 ) !Air.Inst.Ref {
  30872     const pt = sema.pt;
  30873     const zcu = pt.zcu;
  30874     const inst_ty = sema.typeOf(inst);
  30875     const target = zcu.getTarget();
  30876 
  30877     // try coercion of the whole array
  30878     const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, null);
  30879     if (in_memory_result == .ok) {
  30880         if (try sema.resolveValue(inst)) |inst_val| {
  30881             // These types share the same comptime value representation.
  30882             return sema.coerceInMemory(inst_val, dest_ty);
  30883         }
  30884         try sema.requireRuntimeBlock(block, inst_src, null);
  30885         return block.addBitCast(dest_ty, inst);
  30886     }
  30887 
  30888     // otherwise, try element by element
  30889     const inst_len = inst_ty.arrayLen(zcu);
  30890     const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu));
  30891     if (dest_len != inst_len) {
  30892         const msg = msg: {
  30893             const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{
  30894                 dest_ty.fmt(pt), inst_ty.fmt(pt),
  30895             });
  30896             errdefer msg.destroy(sema.gpa);
  30897             try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
  30898             try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
  30899             break :msg msg;
  30900         };
  30901         return sema.failWithOwnedErrorMsg(block, msg);
  30902     }
  30903 
  30904     const dest_elem_ty = dest_ty.childType(zcu);
  30905     if (dest_ty.isVector(zcu) and inst_ty.isVector(zcu) and (try sema.resolveValue(inst)) == null) {
  30906         const inst_elem_ty = inst_ty.childType(zcu);
  30907         switch (dest_elem_ty.zigTypeTag(zcu)) {
  30908             .int => if (inst_elem_ty.isInt(zcu)) {
  30909                 // integer widening
  30910                 const dst_info = dest_elem_ty.intInfo(zcu);
  30911                 const src_info = inst_elem_ty.intInfo(zcu);
  30912                 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
  30913                     // small enough unsigned ints can get casted to large enough signed ints
  30914                     (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
  30915                 {
  30916                     try sema.requireRuntimeBlock(block, inst_src, null);
  30917                     return block.addTyOp(.intcast, dest_ty, inst);
  30918                 }
  30919             },
  30920             .float => if (inst_elem_ty.isRuntimeFloat()) {
  30921                 // float widening
  30922                 const src_bits = inst_elem_ty.floatBits(target);
  30923                 const dst_bits = dest_elem_ty.floatBits(target);
  30924                 if (dst_bits >= src_bits) {
  30925                     try sema.requireRuntimeBlock(block, inst_src, null);
  30926                     return block.addTyOp(.fpext, dest_ty, inst);
  30927                 }
  30928             },
  30929             else => {},
  30930         }
  30931     }
  30932 
  30933     const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
  30934     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
  30935     var runtime_src: ?LazySrcLoc = null;
  30936 
  30937     for (element_vals, element_refs, 0..) |*val, *ref, i| {
  30938         const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern());
  30939         const src = inst_src; // TODO better source location
  30940         const elem_src = inst_src; // TODO better source location
  30941         const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true);
  30942         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  30943         ref.* = coerced;
  30944         if (runtime_src == null) {
  30945             if (try sema.resolveValue(coerced)) |elem_val| {
  30946                 val.* = elem_val.toIntern();
  30947             } else {
  30948                 runtime_src = elem_src;
  30949             }
  30950         }
  30951     }
  30952 
  30953     if (runtime_src) |rs| {
  30954         try sema.requireRuntimeBlock(block, inst_src, rs);
  30955         return block.addAggregateInit(dest_ty, element_refs);
  30956     }
  30957 
  30958     return Air.internedToRef((try pt.aggregateValue(dest_ty, element_vals)).toIntern());
  30959 }
  30960 
  30961 /// If the lengths match, coerces element-wise.
  30962 fn coerceTupleToArray(
  30963     sema: *Sema,
  30964     block: *Block,
  30965     dest_ty: Type,
  30966     dest_ty_src: LazySrcLoc,
  30967     inst: Air.Inst.Ref,
  30968     inst_src: LazySrcLoc,
  30969 ) !Air.Inst.Ref {
  30970     const pt = sema.pt;
  30971     const zcu = pt.zcu;
  30972     const inst_ty = sema.typeOf(inst);
  30973     const inst_len = inst_ty.arrayLen(zcu);
  30974     const dest_len = dest_ty.arrayLen(zcu);
  30975 
  30976     if (dest_len != inst_len) {
  30977         const msg = msg: {
  30978             const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{
  30979                 dest_ty.fmt(pt), inst_ty.fmt(pt),
  30980             });
  30981             errdefer msg.destroy(sema.gpa);
  30982             try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
  30983             try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
  30984             break :msg msg;
  30985         };
  30986         return sema.failWithOwnedErrorMsg(block, msg);
  30987     }
  30988 
  30989     const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len);
  30990     const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
  30991     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
  30992     const dest_elem_ty = dest_ty.childType(zcu);
  30993 
  30994     var runtime_src: ?LazySrcLoc = null;
  30995     for (element_vals, element_refs, 0..) |*val, *ref, i_usize| {
  30996         const i: u32 = @intCast(i_usize);
  30997         if (i_usize == inst_len) {
  30998             const sentinel_val = dest_ty.sentinel(zcu).?;
  30999             val.* = sentinel_val.toIntern();
  31000             ref.* = Air.internedToRef(sentinel_val.toIntern());
  31001             break;
  31002         }
  31003         const elem_src = inst_src; // TODO better source location
  31004         const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i);
  31005         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  31006         ref.* = coerced;
  31007         if (runtime_src == null) {
  31008             if (try sema.resolveValue(coerced)) |elem_val| {
  31009                 val.* = elem_val.toIntern();
  31010             } else {
  31011                 runtime_src = elem_src;
  31012             }
  31013         }
  31014     }
  31015 
  31016     if (runtime_src) |rs| {
  31017         try sema.requireRuntimeBlock(block, inst_src, rs);
  31018         return block.addAggregateInit(dest_ty, element_refs);
  31019     }
  31020 
  31021     return Air.internedToRef((try pt.aggregateValue(dest_ty, element_vals)).toIntern());
  31022 }
  31023 
  31024 /// If the lengths match, coerces element-wise.
  31025 fn coerceTupleToSlicePtrs(
  31026     sema: *Sema,
  31027     block: *Block,
  31028     slice_ty: Type,
  31029     slice_ty_src: LazySrcLoc,
  31030     ptr_tuple: Air.Inst.Ref,
  31031     tuple_src: LazySrcLoc,
  31032 ) !Air.Inst.Ref {
  31033     const pt = sema.pt;
  31034     const zcu = pt.zcu;
  31035     const tuple_ty = sema.typeOf(ptr_tuple).childType(zcu);
  31036     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  31037     const slice_info = slice_ty.ptrInfo(zcu);
  31038     const array_ty = try pt.arrayType(.{
  31039         .len = tuple_ty.structFieldCount(zcu),
  31040         .sentinel = slice_info.sentinel,
  31041         .child = slice_info.child,
  31042     });
  31043     const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
  31044     if (slice_info.flags.alignment != .none) {
  31045         return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  31046     }
  31047     const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst);
  31048     return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
  31049 }
  31050 
  31051 /// If the lengths match, coerces element-wise.
  31052 fn coerceTupleToArrayPtrs(
  31053     sema: *Sema,
  31054     block: *Block,
  31055     ptr_array_ty: Type,
  31056     array_ty_src: LazySrcLoc,
  31057     ptr_tuple: Air.Inst.Ref,
  31058     tuple_src: LazySrcLoc,
  31059 ) !Air.Inst.Ref {
  31060     const pt = sema.pt;
  31061     const zcu = pt.zcu;
  31062     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  31063     const ptr_info = ptr_array_ty.ptrInfo(zcu);
  31064     const array_ty: Type = .fromInterned(ptr_info.child);
  31065     const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src);
  31066     if (ptr_info.flags.alignment != .none) {
  31067         return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  31068     }
  31069     const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst);
  31070     return ptr_array;
  31071 }
  31072 
  31073 fn coerceTupleToTuple(
  31074     sema: *Sema,
  31075     block: *Block,
  31076     tuple_ty: Type,
  31077     inst: Air.Inst.Ref,
  31078     inst_src: LazySrcLoc,
  31079 ) !Air.Inst.Ref {
  31080     const pt = sema.pt;
  31081     const zcu = pt.zcu;
  31082     const ip = &zcu.intern_pool;
  31083     const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) {
  31084         .tuple_type => |tuple_type| tuple_type.types.len,
  31085         else => unreachable,
  31086     };
  31087     const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count);
  31088     const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
  31089     @memset(field_refs, .none);
  31090 
  31091     const inst_ty = sema.typeOf(inst);
  31092     const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) {
  31093         .tuple_type => |tuple_type| tuple_type.types.len,
  31094         else => unreachable,
  31095     };
  31096     if (src_field_count > dest_field_count) return error.NotCoercible;
  31097 
  31098     var runtime_src: ?LazySrcLoc = null;
  31099     for (0..dest_field_count) |field_index_usize| {
  31100         const field_i: u32 = @intCast(field_index_usize);
  31101         const field_src = inst_src; // TODO better source location
  31102 
  31103         const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) {
  31104             .tuple_type => |tuple_type| tuple_type.types.get(ip)[field_index_usize],
  31105             .struct_type => ip.loadStructType(tuple_ty.toIntern()).field_types.get(ip)[field_index_usize],
  31106             else => unreachable,
  31107         };
  31108         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  31109             .tuple_type => |tuple_type| tuple_type.values.get(ip)[field_index_usize],
  31110             .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, field_index_usize),
  31111             else => unreachable,
  31112         };
  31113 
  31114         const field_index: u32 = @intCast(field_index_usize);
  31115 
  31116         const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
  31117         const coerced = try sema.coerce(block, .fromInterned(field_ty), elem_ref, field_src);
  31118         field_refs[field_index] = coerced;
  31119         if (default_val != .none) {
  31120             const init_val = (try sema.resolveValue(coerced)) orelse {
  31121                 return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field });
  31122             };
  31123 
  31124             if (!init_val.eql(Value.fromInterned(default_val), .fromInterned(field_ty), pt.zcu)) {
  31125                 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
  31126             }
  31127         }
  31128         if (runtime_src == null) {
  31129             if (try sema.resolveValue(coerced)) |field_val| {
  31130                 field_vals[field_index] = field_val.toIntern();
  31131             } else {
  31132                 runtime_src = field_src;
  31133             }
  31134         }
  31135     }
  31136 
  31137     // Populate default field values and report errors for missing fields.
  31138     var root_msg: ?*Zcu.ErrorMsg = null;
  31139     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  31140 
  31141     for (field_refs, 0..) |*field_ref, i_usize| {
  31142         const i: u32 = @intCast(i_usize);
  31143         if (field_ref.* != .none) continue;
  31144 
  31145         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  31146             .tuple_type => |tuple_type| tuple_type.values.get(ip)[i],
  31147             .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, i),
  31148             else => unreachable,
  31149         };
  31150 
  31151         const field_src = inst_src; // TODO better source location
  31152         if (default_val == .none) {
  31153             const template = "missing tuple field: {d}";
  31154             if (root_msg) |msg| {
  31155                 try sema.errNote(field_src, msg, template, .{i});
  31156             } else {
  31157                 root_msg = try sema.errMsg(field_src, template, .{i});
  31158             }
  31159             continue;
  31160         }
  31161         if (runtime_src == null) {
  31162             field_vals[i] = default_val;
  31163         } else {
  31164             field_ref.* = Air.internedToRef(default_val);
  31165         }
  31166     }
  31167 
  31168     if (root_msg) |msg| {
  31169         try sema.addDeclaredHereNote(msg, tuple_ty);
  31170         root_msg = null;
  31171         return sema.failWithOwnedErrorMsg(block, msg);
  31172     }
  31173 
  31174     if (runtime_src) |rs| {
  31175         try sema.requireRuntimeBlock(block, inst_src, rs);
  31176         return block.addAggregateInit(tuple_ty, field_refs);
  31177     }
  31178 
  31179     return Air.internedToRef((try pt.aggregateValue(tuple_ty, field_vals)).toIntern());
  31180 }
  31181 
  31182 fn analyzeNavVal(
  31183     sema: *Sema,
  31184     block: *Block,
  31185     src: LazySrcLoc,
  31186     nav_index: InternPool.Nav.Index,
  31187 ) CompileError!Air.Inst.Ref {
  31188     const ref = try sema.analyzeNavRefInner(block, src, nav_index, false);
  31189     return sema.analyzeLoad(block, src, ref, src);
  31190 }
  31191 
  31192 fn addReferenceEntry(
  31193     sema: *Sema,
  31194     opt_block: ?*Block,
  31195     src: LazySrcLoc,
  31196     referenced_unit: AnalUnit,
  31197 ) !void {
  31198     const zcu = sema.pt.zcu;
  31199     const ip = &zcu.intern_pool;
  31200     switch (referenced_unit.unwrap()) {
  31201         .func => |f| assert(ip.unwrapCoercedFunc(f) == f), // for `.{ .func = f }`, `f` must be uncoerced
  31202         else => {},
  31203     }
  31204     if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
  31205     const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
  31206     if (gop.found_existing) return;
  31207     try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: {
  31208         const block = opt_block orelse break :inline_frame .none;
  31209         const inlining = block.inlining orelse break :inline_frame .none;
  31210         const frame = try inlining.refFrame(zcu);
  31211         break :inline_frame frame.toOptional();
  31212     });
  31213 }
  31214 
  31215 pub fn addTypeReferenceEntry(
  31216     sema: *Sema,
  31217     src: LazySrcLoc,
  31218     referenced_type: InternPool.Index,
  31219 ) !void {
  31220     const zcu = sema.pt.zcu;
  31221     if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
  31222     const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type);
  31223     if (gop.found_existing) return;
  31224     try zcu.addTypeReference(sema.owner, referenced_type, src);
  31225 }
  31226 
  31227 fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.MemoizedStateStage) SemaError!void {
  31228     const pt = sema.pt;
  31229 
  31230     const unit: AnalUnit = .wrap(.{ .memoized_state = stage });
  31231     try sema.addReferenceEntry(null, src, unit);
  31232     try sema.declareDependency(.{ .memoized_state = stage });
  31233 
  31234     if (pt.zcu.analysis_in_progress.contains(unit)) {
  31235         return sema.failWithOwnedErrorMsg(null, try sema.errMsg(src, "dependency loop detected", .{}));
  31236     }
  31237     try pt.ensureMemoizedStateUpToDate(stage);
  31238 }
  31239 
  31240 pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void {
  31241     const pt = sema.pt;
  31242     const zcu = pt.zcu;
  31243     const ip = &zcu.intern_pool;
  31244 
  31245     const nav = ip.getNav(nav_index);
  31246     if (nav.analysis == null) {
  31247         assert(nav.status == .fully_resolved);
  31248         return;
  31249     }
  31250 
  31251     try sema.declareDependency(switch (kind) {
  31252         .type => .{ .nav_ty = nav_index },
  31253         .fully => .{ .nav_val = nav_index },
  31254     });
  31255 
  31256     // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate`
  31257     // to make sure the value is up-to-date on incremental updates.
  31258 
  31259     const anal_unit: AnalUnit = .wrap(switch (kind) {
  31260         .type => .{ .nav_ty = nav_index },
  31261         .fully => .{ .nav_val = nav_index },
  31262     });
  31263     try sema.addReferenceEntry(block, src, anal_unit);
  31264 
  31265     if (zcu.analysis_in_progress.contains(anal_unit)) {
  31266         return sema.failWithOwnedErrorMsg(null, try sema.errMsg(.{
  31267             .base_node_inst = nav.analysis.?.zir_index,
  31268             .offset = LazySrcLoc.Offset.nodeOffset(.zero),
  31269         }, "dependency loop detected", .{}));
  31270     }
  31271 
  31272     switch (kind) {
  31273         .type => {
  31274             try zcu.ensureNavValAnalysisQueued(nav_index);
  31275             return pt.ensureNavTypeUpToDate(nav_index);
  31276         },
  31277         .fully => return pt.ensureNavValUpToDate(nav_index),
  31278     }
  31279 }
  31280 
  31281 fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
  31282     const pt = sema.pt;
  31283     const ptr_anyopaque_ty = try pt.singleConstPtrType(.anyopaque);
  31284     return Value.fromInterned(try pt.intern(.{ .opt = .{
  31285         .ty = (try pt.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(),
  31286         .val = if (opt_val) |val| (try pt.getCoerced(
  31287             Value.fromInterned(try pt.refValue(val.toIntern())),
  31288             ptr_anyopaque_ty,
  31289         )).toIntern() else .none,
  31290     } }));
  31291 }
  31292 
  31293 fn analyzeNavRef(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!Air.Inst.Ref {
  31294     return sema.analyzeNavRefInner(block, src, nav_index, true);
  31295 }
  31296 
  31297 /// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed.
  31298 /// If this pointer will be used directly, `is_ref` must be `true`.
  31299 /// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`.
  31300 fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref {
  31301     const pt = sema.pt;
  31302     const zcu = pt.zcu;
  31303     const ip = &zcu.intern_pool;
  31304 
  31305     try sema.ensureNavResolved(block, src, orig_nav_index, if (is_ref) .type else .fully);
  31306 
  31307     const nav_index = nav: {
  31308         if (ip.getNav(orig_nav_index).isExternOrFn(ip)) {
  31309             // Getting a pointer to this `Nav` might mean we actually get a pointer to something else!
  31310             // We need to resolve the value to know for sure.
  31311             if (is_ref) try sema.ensureNavResolved(block, src, orig_nav_index, .fully);
  31312             switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
  31313                 .func => |f| break :nav f.owner_nav,
  31314                 .@"extern" => |e| break :nav e.owner_nav,
  31315                 else => {},
  31316             }
  31317         }
  31318         break :nav orig_nav_index;
  31319     };
  31320 
  31321     const nav_status = ip.getNav(nav_index).status;
  31322 
  31323     const is_runtime = switch (nav_status) {
  31324         .unresolved => unreachable,
  31325         // dllimports go straight to `fully_resolved`; the only option is threadlocal
  31326         .type_resolved => |r| r.is_threadlocal,
  31327         .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
  31328             .@"extern" => |e| e.is_threadlocal or e.is_dll_import or switch (e.relocation) {
  31329                 .any => false,
  31330                 .pcrel => true,
  31331             },
  31332             .variable => |v| v.is_threadlocal,
  31333             else => false,
  31334         },
  31335     };
  31336 
  31337     const ty, const alignment, const @"addrspace", const is_const = switch (nav_status) {
  31338         .unresolved => unreachable,
  31339         .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
  31340         .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const },
  31341     };
  31342     const ptr_ty = try pt.ptrTypeSema(.{
  31343         .child = ty,
  31344         .flags = .{
  31345             .alignment = alignment,
  31346             .is_const = is_const,
  31347             .address_space = @"addrspace",
  31348         },
  31349     });
  31350 
  31351     if (is_runtime) {
  31352         // This pointer is runtime-known; we need to emit an AIR instruction to create it.
  31353         return block.addInst(.{
  31354             .tag = .runtime_nav_ptr,
  31355             .data = .{ .ty_nav = .{
  31356                 .ty = ptr_ty.toIntern(),
  31357                 .nav = nav_index,
  31358             } },
  31359         });
  31360     }
  31361 
  31362     if (is_ref) {
  31363         try sema.maybeQueueFuncBodyAnalysis(block, src, nav_index);
  31364     }
  31365 
  31366     return Air.internedToRef((try pt.intern(.{ .ptr = .{
  31367         .ty = ptr_ty.toIntern(),
  31368         .base_addr = .{ .nav = nav_index },
  31369         .byte_offset = 0,
  31370     } })));
  31371 }
  31372 
  31373 fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void {
  31374     const pt = sema.pt;
  31375     const zcu = pt.zcu;
  31376     const ip = &zcu.intern_pool;
  31377 
  31378     // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function.
  31379     // If it is, we can resolve the *value*, and queue analysis as needed.
  31380 
  31381     try sema.ensureNavResolved(block, src, nav_index, .type);
  31382     const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip));
  31383     if (nav_ty.zigTypeTag(zcu) != .@"fn") return;
  31384     if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return;
  31385 
  31386     try sema.ensureNavResolved(block, src, nav_index, .fully);
  31387     const nav_val = zcu.navValue(nav_index);
  31388     if (!ip.isFuncBody(nav_val.toIntern())) return;
  31389 
  31390     const orig_fn_index = ip.unwrapCoercedFunc(nav_val.toIntern());
  31391     try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index }));
  31392     try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
  31393 }
  31394 
  31395 fn analyzeRef(
  31396     sema: *Sema,
  31397     block: *Block,
  31398     src: LazySrcLoc,
  31399     operand: Air.Inst.Ref,
  31400 ) CompileError!Air.Inst.Ref {
  31401     const pt = sema.pt;
  31402     const zcu = pt.zcu;
  31403     const operand_ty = sema.typeOf(operand);
  31404 
  31405     if (try sema.resolveValue(operand)) |val| {
  31406         switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  31407             .@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav),
  31408             .func => |f| return sema.analyzeNavRef(block, src, f.owner_nav),
  31409             else => return uavRef(sema, val.toIntern()),
  31410         }
  31411     }
  31412 
  31413     // No `requireRuntimeBlock`; it's okay to `ref` to a runtime value in a comptime context,
  31414     // it's just that we can only use the *type* of the result, since the value is runtime-known.
  31415 
  31416     const address_space = target_util.defaultAddressSpace(zcu.getTarget(), .local);
  31417     const ptr_type = try pt.ptrTypeSema(.{
  31418         .child = operand_ty.toIntern(),
  31419         .flags = .{
  31420             .is_const = true,
  31421             .address_space = address_space,
  31422         },
  31423     });
  31424     const mut_ptr_type = try pt.ptrTypeSema(.{
  31425         .child = operand_ty.toIntern(),
  31426         .flags = .{ .address_space = address_space },
  31427     });
  31428     const alloc = try block.addTy(.alloc, mut_ptr_type);
  31429 
  31430     // In a comptime context, the store would fail, since the operand is runtime-known. But that's
  31431     // okay; we don't actually need this store to succeed, since we're creating a runtime value in a
  31432     // comptime scope, so the value can never be used aside from to get its type.
  31433     if (!block.isComptime()) {
  31434         try sema.storePtr(block, src, alloc, operand);
  31435     }
  31436 
  31437     // Cast to the constant pointer type. We do this directly rather than going via `coerce` to
  31438     // avoid errors in the `block.isComptime()` case.
  31439     return block.addBitCast(ptr_type, alloc);
  31440 }
  31441 
  31442 fn analyzeLoad(
  31443     sema: *Sema,
  31444     block: *Block,
  31445     src: LazySrcLoc,
  31446     ptr: Air.Inst.Ref,
  31447     ptr_src: LazySrcLoc,
  31448 ) CompileError!Air.Inst.Ref {
  31449     const pt = sema.pt;
  31450     const zcu = pt.zcu;
  31451     const ptr_ty = sema.typeOf(ptr);
  31452     const elem_ty = switch (ptr_ty.zigTypeTag(zcu)) {
  31453         .pointer => ptr_ty.childType(zcu),
  31454         else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}),
  31455     };
  31456     if (elem_ty.zigTypeTag(zcu) == .@"opaque") {
  31457         return sema.fail(block, ptr_src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)});
  31458     }
  31459 
  31460     if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| {
  31461         return Air.internedToRef(opv.toIntern());
  31462     }
  31463 
  31464     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  31465         if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| {
  31466             return Air.internedToRef(elem_val.toIntern());
  31467         }
  31468     }
  31469 
  31470     return block.addTyOp(.load, elem_ty, ptr);
  31471 }
  31472 
  31473 fn analyzeSlicePtr(
  31474     sema: *Sema,
  31475     block: *Block,
  31476     slice_src: LazySrcLoc,
  31477     slice: Air.Inst.Ref,
  31478     slice_ty: Type,
  31479 ) CompileError!Air.Inst.Ref {
  31480     const pt = sema.pt;
  31481     const zcu = pt.zcu;
  31482     const result_ty = slice_ty.slicePtrFieldType(zcu);
  31483     if (try sema.resolveValue(slice)) |val| {
  31484         if (val.isUndef(zcu)) return pt.undefRef(result_ty);
  31485         return Air.internedToRef(val.slicePtr(zcu).toIntern());
  31486     }
  31487     try sema.requireRuntimeBlock(block, slice_src, null);
  31488     return block.addTyOp(.slice_ptr, result_ty, slice);
  31489 }
  31490 
  31491 fn analyzeOptionalSlicePtr(
  31492     sema: *Sema,
  31493     block: *Block,
  31494     opt_slice_src: LazySrcLoc,
  31495     opt_slice: Air.Inst.Ref,
  31496     opt_slice_ty: Type,
  31497 ) CompileError!Air.Inst.Ref {
  31498     const pt = sema.pt;
  31499     const zcu = pt.zcu;
  31500     const slice_ty = opt_slice_ty.optionalChild(zcu);
  31501     const result_ty = slice_ty.slicePtrFieldType(zcu);
  31502 
  31503     if (try sema.resolveValue(opt_slice)) |opt_val| {
  31504         if (opt_val.isUndef(zcu)) return pt.undefRef(result_ty);
  31505         const slice_ptr: InternPool.Index = if (opt_val.optionalValue(zcu)) |val|
  31506             val.slicePtr(zcu).toIntern()
  31507         else
  31508             .null_value;
  31509 
  31510         return Air.internedToRef(slice_ptr);
  31511     }
  31512 
  31513     try sema.requireRuntimeBlock(block, opt_slice_src, null);
  31514 
  31515     const slice = try block.addTyOp(.optional_payload, slice_ty, opt_slice);
  31516     return block.addTyOp(.slice_ptr, result_ty, slice);
  31517 }
  31518 
  31519 fn analyzeSliceLen(
  31520     sema: *Sema,
  31521     block: *Block,
  31522     src: LazySrcLoc,
  31523     slice_inst: Air.Inst.Ref,
  31524 ) CompileError!Air.Inst.Ref {
  31525     const pt = sema.pt;
  31526     const zcu = pt.zcu;
  31527     if (try sema.resolveValue(slice_inst)) |slice_val| {
  31528         if (slice_val.isUndef(zcu)) {
  31529             return .undef_usize;
  31530         }
  31531         return pt.intRef(.usize, try slice_val.sliceLen(pt));
  31532     }
  31533     try sema.requireRuntimeBlock(block, src, null);
  31534     return block.addTyOp(.slice_len, .usize, slice_inst);
  31535 }
  31536 
  31537 fn analyzeIsNull(
  31538     sema: *Sema,
  31539     block: *Block,
  31540     operand: Air.Inst.Ref,
  31541     invert_logic: bool,
  31542 ) CompileError!Air.Inst.Ref {
  31543     const pt = sema.pt;
  31544     const zcu = pt.zcu;
  31545     const result_ty: Type = .bool;
  31546     if (try sema.resolveValue(operand)) |opt_val| {
  31547         if (opt_val.isUndef(zcu)) {
  31548             return pt.undefRef(result_ty);
  31549         }
  31550         const is_null = opt_val.isNull(zcu);
  31551         const bool_value = if (invert_logic) !is_null else is_null;
  31552         return if (bool_value) .bool_true else .bool_false;
  31553     }
  31554 
  31555     if (sema.typeOf(operand).isNullFromType(zcu)) |is_null| {
  31556         const result = is_null != invert_logic;
  31557         return if (result) .bool_true else .bool_false;
  31558     }
  31559     const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
  31560     return block.addUnOp(air_tag, operand);
  31561 }
  31562 
  31563 fn analyzePtrIsNonErrComptimeOnly(
  31564     sema: *Sema,
  31565     block: *Block,
  31566     src: LazySrcLoc,
  31567     operand: Air.Inst.Ref,
  31568 ) CompileError!Air.Inst.Ref {
  31569     const pt = sema.pt;
  31570     const zcu = pt.zcu;
  31571     const ptr_ty = sema.typeOf(operand);
  31572     assert(ptr_ty.zigTypeTag(zcu) == .pointer);
  31573     const child_ty = ptr_ty.childType(zcu);
  31574 
  31575     const child_tag = child_ty.zigTypeTag(zcu);
  31576     if (child_tag != .error_set and child_tag != .error_union) return .bool_true;
  31577     if (child_tag == .error_set) return .bool_false;
  31578     assert(child_tag == .error_union);
  31579 
  31580     _ = block;
  31581     _ = src;
  31582 
  31583     return .none;
  31584 }
  31585 
  31586 fn analyzeIsNonErrComptimeOnly(
  31587     sema: *Sema,
  31588     block: *Block,
  31589     src: LazySrcLoc,
  31590     operand: Air.Inst.Ref,
  31591 ) CompileError!Air.Inst.Ref {
  31592     const pt = sema.pt;
  31593     const zcu = pt.zcu;
  31594     const ip = &zcu.intern_pool;
  31595     const operand_ty = sema.typeOf(operand);
  31596     const ot = operand_ty.zigTypeTag(zcu);
  31597     if (ot != .error_set and ot != .error_union) return .bool_true;
  31598     if (ot == .error_set) return .bool_false;
  31599     assert(ot == .error_union);
  31600 
  31601     const payload_ty = operand_ty.errorUnionPayload(zcu);
  31602     if (payload_ty.zigTypeTag(zcu) == .noreturn) {
  31603         return .bool_false;
  31604     }
  31605 
  31606     if (operand.toIndex()) |operand_inst| {
  31607         switch (sema.air_instructions.items(.tag)[@intFromEnum(operand_inst)]) {
  31608             .wrap_errunion_payload => return .bool_true,
  31609             .wrap_errunion_err => return .bool_false,
  31610             else => {},
  31611         }
  31612     } else if (operand == .undef) {
  31613         return .undef_bool;
  31614     } else if (@intFromEnum(operand) < InternPool.static_len) {
  31615         // None of the ref tags can be errors.
  31616         return .bool_true;
  31617     }
  31618 
  31619     const maybe_operand_val = try sema.resolveValue(operand);
  31620 
  31621     // exception if the error union error set is known to be empty,
  31622     // we allow the comparison but always make it comptime-known.
  31623     const set_ty = ip.errorUnionSet(operand_ty.toIntern());
  31624     switch (set_ty) {
  31625         .anyerror_type => {},
  31626         .adhoc_inferred_error_set_type => if (sema.fn_ret_ty_ies) |ies| blk: {
  31627             // If the error set is empty, we must return a comptime true or false.
  31628             // However we want to avoid unnecessarily resolving an inferred error set
  31629             // in case it is already non-empty.
  31630             switch (ies.resolved) {
  31631                 .anyerror_type => break :blk,
  31632                 .none => {},
  31633                 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
  31634             }
  31635 
  31636             if (maybe_operand_val != null) break :blk;
  31637 
  31638             // Try to avoid resolving inferred error set if possible.
  31639             if (ies.errors.count() != 0) return .none;
  31640             switch (ies.resolved) {
  31641                 .anyerror_type => return .none,
  31642                 .none => {},
  31643                 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
  31644                     0 => return .bool_true,
  31645                     else => return .none,
  31646                 },
  31647             }
  31648             // We do not have a comptime answer because this inferred error
  31649             // set is not resolved, and an instruction later in this function
  31650             // body may or may not cause an error to be added to this set.
  31651             return .none;
  31652         },
  31653         else => switch (ip.indexToKey(set_ty)) {
  31654             .error_set_type => |error_set_type| {
  31655                 if (error_set_type.names.len == 0) return .bool_true;
  31656             },
  31657             .inferred_error_set_type => |func_index| blk: {
  31658                 // If the error set is empty, we must return a comptime true or false.
  31659                 // However we want to avoid unnecessarily resolving an inferred error set
  31660                 // in case it is already non-empty.
  31661                 try zcu.maybeUnresolveIes(func_index);
  31662                 switch (ip.funcIesResolvedUnordered(func_index)) {
  31663                     .anyerror_type => break :blk,
  31664                     .none => {},
  31665                     else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
  31666                 }
  31667                 if (maybe_operand_val != null) break :blk;
  31668                 if (sema.fn_ret_ty_ies) |ies| {
  31669                     if (ies.func == func_index) {
  31670                         // Try to avoid resolving inferred error set if possible.
  31671                         if (ies.errors.count() != 0) return .none;
  31672                         switch (ies.resolved) {
  31673                             .anyerror_type => return .none,
  31674                             .none => {},
  31675                             else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
  31676                                 0 => return .bool_true,
  31677                                 else => return .none,
  31678                             },
  31679                         }
  31680                         // We do not have a comptime answer because this inferred error
  31681                         // set is not resolved, and an instruction later in this function
  31682                         // body may or may not cause an error to be added to this set.
  31683                         return .none;
  31684                     }
  31685                 }
  31686                 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);
  31687                 if (resolved_ty == .anyerror_type)
  31688                     break :blk;
  31689                 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0)
  31690                     return .bool_true;
  31691             },
  31692             else => unreachable,
  31693         },
  31694     }
  31695 
  31696     if (maybe_operand_val) |err_union| {
  31697         return if (err_union.isUndef(zcu)) .undef_bool else if (err_union.getErrorName(zcu) == .none) .bool_true else .bool_false;
  31698     }
  31699     return .none;
  31700 }
  31701 
  31702 fn analyzeIsNonErr(
  31703     sema: *Sema,
  31704     block: *Block,
  31705     src: LazySrcLoc,
  31706     operand: Air.Inst.Ref,
  31707 ) CompileError!Air.Inst.Ref {
  31708     const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
  31709     if (result == .none) {
  31710         try sema.requireRuntimeBlock(block, src, null);
  31711         return block.addUnOp(.is_non_err, operand);
  31712     } else {
  31713         return result;
  31714     }
  31715 }
  31716 
  31717 fn analyzePtrIsNonErr(
  31718     sema: *Sema,
  31719     block: *Block,
  31720     src: LazySrcLoc,
  31721     operand: Air.Inst.Ref,
  31722 ) CompileError!Air.Inst.Ref {
  31723     const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);
  31724     if (result == .none) {
  31725         try sema.requireRuntimeBlock(block, src, null);
  31726         return block.addUnOp(.is_non_err_ptr, operand);
  31727     } else {
  31728         return result;
  31729     }
  31730 }
  31731 
  31732 fn analyzeSlice(
  31733     sema: *Sema,
  31734     block: *Block,
  31735     src: LazySrcLoc,
  31736     ptr_ptr: Air.Inst.Ref,
  31737     uncasted_start: Air.Inst.Ref,
  31738     uncasted_end_opt: Air.Inst.Ref,
  31739     sentinel_opt: Air.Inst.Ref,
  31740     sentinel_src: LazySrcLoc,
  31741     ptr_src: LazySrcLoc,
  31742     start_src: LazySrcLoc,
  31743     end_src: LazySrcLoc,
  31744     by_length: bool,
  31745 ) CompileError!Air.Inst.Ref {
  31746     const pt = sema.pt;
  31747     const zcu = pt.zcu;
  31748     // Slice expressions can operate on a variable whose type is an array. This requires
  31749     // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
  31750     const ptr_ptr_ty = sema.typeOf(ptr_ptr);
  31751     const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(zcu)) {
  31752         .pointer => ptr_ptr_ty.childType(zcu),
  31753         else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ptr_ty.fmt(pt)}),
  31754     };
  31755 
  31756     var array_ty = ptr_ptr_child_ty;
  31757     var slice_ty = ptr_ptr_ty;
  31758     var ptr_or_slice = ptr_ptr;
  31759     var elem_ty: Type = undefined;
  31760     var ptr_sentinel: ?Value = null;
  31761     switch (ptr_ptr_child_ty.zigTypeTag(zcu)) {
  31762         .array => {
  31763             ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu);
  31764             elem_ty = ptr_ptr_child_ty.childType(zcu);
  31765         },
  31766         .pointer => switch (ptr_ptr_child_ty.ptrSize(zcu)) {
  31767             .one => {
  31768                 const double_child_ty = ptr_ptr_child_ty.childType(zcu);
  31769                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  31770                 if (double_child_ty.zigTypeTag(zcu) == .array) {
  31771                     ptr_sentinel = double_child_ty.sentinel(zcu);
  31772                     slice_ty = ptr_ptr_child_ty;
  31773                     array_ty = double_child_ty;
  31774                     elem_ty = double_child_ty.childType(zcu);
  31775                 } else {
  31776                     if (uncasted_end_opt == .none) {
  31777                         return sema.fail(block, src, "slice of single-item pointer must be bounded", .{});
  31778                     }
  31779                     const start_value = try sema.resolveConstDefinedValue(
  31780                         block,
  31781                         start_src,
  31782                         uncasted_start,
  31783                         .{ .simple = .slice_single_item_ptr_bounds },
  31784                     );
  31785 
  31786                     const end_value = try sema.resolveConstDefinedValue(
  31787                         block,
  31788                         end_src,
  31789                         uncasted_end_opt,
  31790                         .{ .simple = .slice_single_item_ptr_bounds },
  31791                     );
  31792 
  31793                     const bounds_error_message = "slice of single-item pointer must have bounds [0..0], [0..1], or [1..1]";
  31794                     if (try sema.compareScalar(start_value, .neq, end_value, .comptime_int)) {
  31795                         if (try sema.compareScalar(start_value, .neq, Value.zero_comptime_int, .comptime_int)) {
  31796                             const msg = msg: {
  31797                                 const msg = try sema.errMsg(start_src, bounds_error_message, .{});
  31798                                 errdefer msg.destroy(sema.gpa);
  31799                                 try sema.errNote(
  31800                                     start_src,
  31801                                     msg,
  31802                                     "expected '{f}', found '{f}'",
  31803                                     .{
  31804                                         Value.zero_comptime_int.fmtValueSema(pt, sema),
  31805                                         start_value.fmtValueSema(pt, sema),
  31806                                     },
  31807                                 );
  31808                                 break :msg msg;
  31809                             };
  31810                             return sema.failWithOwnedErrorMsg(block, msg);
  31811                         } else if (try sema.compareScalar(end_value, .neq, Value.one_comptime_int, .comptime_int)) {
  31812                             const msg = msg: {
  31813                                 const msg = try sema.errMsg(end_src, bounds_error_message, .{});
  31814                                 errdefer msg.destroy(sema.gpa);
  31815                                 try sema.errNote(
  31816                                     end_src,
  31817                                     msg,
  31818                                     "expected '{f}', found '{f}'",
  31819                                     .{
  31820                                         Value.one_comptime_int.fmtValueSema(pt, sema),
  31821                                         end_value.fmtValueSema(pt, sema),
  31822                                     },
  31823                                 );
  31824                                 break :msg msg;
  31825                             };
  31826                             return sema.failWithOwnedErrorMsg(block, msg);
  31827                         }
  31828                     } else {
  31829                         if (try sema.compareScalar(end_value, .gt, Value.one_comptime_int, .comptime_int)) {
  31830                             return sema.fail(
  31831                                 block,
  31832                                 end_src,
  31833                                 "end index {f} out of bounds for slice of single-item pointer",
  31834                                 .{end_value.fmtValueSema(pt, sema)},
  31835                             );
  31836                         }
  31837                     }
  31838 
  31839                     array_ty = try pt.arrayType(.{
  31840                         .len = 1,
  31841                         .child = double_child_ty.toIntern(),
  31842                     });
  31843                     const ptr_info = ptr_ptr_child_ty.ptrInfo(zcu);
  31844                     slice_ty = try pt.ptrType(.{
  31845                         .child = array_ty.toIntern(),
  31846                         .flags = .{
  31847                             .alignment = ptr_info.flags.alignment,
  31848                             .is_const = ptr_info.flags.is_const,
  31849                             .is_allowzero = ptr_info.flags.is_allowzero,
  31850                             .is_volatile = ptr_info.flags.is_volatile,
  31851                             .address_space = ptr_info.flags.address_space,
  31852                         },
  31853                     });
  31854                     elem_ty = double_child_ty;
  31855                 }
  31856             },
  31857             .many, .c => {
  31858                 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu);
  31859                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  31860                 slice_ty = ptr_ptr_child_ty;
  31861                 array_ty = ptr_ptr_child_ty;
  31862                 elem_ty = ptr_ptr_child_ty.childType(zcu);
  31863 
  31864                 if (ptr_ptr_child_ty.ptrSize(zcu) == .c) {
  31865                     if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| {
  31866                         if (ptr_val.isNull(zcu)) {
  31867                             return sema.fail(block, src, "slice of null pointer", .{});
  31868                         }
  31869                     }
  31870                 }
  31871             },
  31872             .slice => {
  31873                 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu);
  31874                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  31875                 slice_ty = ptr_ptr_child_ty;
  31876                 array_ty = ptr_ptr_child_ty;
  31877                 elem_ty = ptr_ptr_child_ty.childType(zcu);
  31878             },
  31879         },
  31880         else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}),
  31881     }
  31882 
  31883     const ptr = if (slice_ty.isSlice(zcu))
  31884         try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty)
  31885     else if (array_ty.zigTypeTag(zcu) == .array) ptr: {
  31886         var manyptr_ty_key = zcu.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type;
  31887         assert(manyptr_ty_key.child == array_ty.toIntern());
  31888         assert(manyptr_ty_key.flags.size == .one);
  31889         manyptr_ty_key.child = elem_ty.toIntern();
  31890         manyptr_ty_key.flags.size = .many;
  31891         break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(manyptr_ty_key), ptr_or_slice, ptr_src);
  31892     } else ptr_or_slice;
  31893 
  31894     const start = try sema.coerce(block, .usize, uncasted_start, start_src);
  31895     const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
  31896     const new_ptr_ty = sema.typeOf(new_ptr);
  31897 
  31898     // true if and only if the end index of the slice, implicitly or explicitly, equals
  31899     // the length of the underlying object being sliced. we might learn the length of the
  31900     // underlying object because it is an array (which has the length in the type), or
  31901     // we might learn of the length because it is a comptime-known slice value.
  31902     var end_is_len = uncasted_end_opt == .none;
  31903     const end = e: {
  31904         if (array_ty.zigTypeTag(zcu) == .array) {
  31905             const len_val = try pt.intValue(.usize, array_ty.arrayLen(zcu));
  31906 
  31907             if (!end_is_len) {
  31908                 const end = if (by_length) end: {
  31909                     const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31910                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  31911                     break :end try sema.coerce(block, .usize, uncasted_end, end_src);
  31912                 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31913                 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  31914                     const len_s_val = try pt.intValue(
  31915                         .usize,
  31916                         array_ty.arrayLenIncludingSentinel(zcu),
  31917                     );
  31918                     if (!(try sema.compareAll(end_val, .lte, len_s_val, .usize))) {
  31919                         const sentinel_label: []const u8 = if (array_ty.sentinel(zcu) != null)
  31920                             " +1 (sentinel)"
  31921                         else
  31922                             "";
  31923 
  31924                         return sema.fail(
  31925                             block,
  31926                             end_src,
  31927                             "end index {f} out of bounds for array of length {f}{s}",
  31928                             .{
  31929                                 end_val.fmtValueSema(pt, sema),
  31930                                 len_val.fmtValueSema(pt, sema),
  31931                                 sentinel_label,
  31932                             },
  31933                         );
  31934                     }
  31935 
  31936                     // end_is_len is only true if we are NOT using the sentinel
  31937                     // length. For sentinel-length, we don't want the type to
  31938                     // contain the sentinel.
  31939                     if (end_val.eql(len_val, .usize, zcu)) {
  31940                         end_is_len = true;
  31941                     }
  31942                 }
  31943                 break :e end;
  31944             }
  31945 
  31946             break :e Air.internedToRef(len_val.toIntern());
  31947         } else if (slice_ty.isSlice(zcu)) {
  31948             if (!end_is_len) {
  31949                 const end = if (by_length) end: {
  31950                     const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31951                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  31952                     break :end try sema.coerce(block, .usize, uncasted_end, end_src);
  31953                 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31954                 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  31955                     if (try sema.resolveValue(ptr_or_slice)) |slice_val| {
  31956                         if (slice_val.isUndef(zcu)) {
  31957                             return sema.fail(block, src, "slice of undefined", .{});
  31958                         }
  31959                         const has_sentinel = slice_ty.sentinel(zcu) != null;
  31960                         const slice_len = try slice_val.sliceLen(pt);
  31961                         const len_plus_sent = slice_len + @intFromBool(has_sentinel);
  31962                         const slice_len_val_with_sentinel = try pt.intValue(.usize, len_plus_sent);
  31963                         if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, .usize))) {
  31964                             const sentinel_label: []const u8 = if (has_sentinel)
  31965                                 " +1 (sentinel)"
  31966                             else
  31967                                 "";
  31968 
  31969                             return sema.fail(
  31970                                 block,
  31971                                 end_src,
  31972                                 "end index {f} out of bounds for slice of length {d}{s}",
  31973                                 .{
  31974                                     end_val.fmtValueSema(pt, sema),
  31975                                     try slice_val.sliceLen(pt),
  31976                                     sentinel_label,
  31977                                 },
  31978                             );
  31979                         }
  31980 
  31981                         // If the slice has a sentinel, we consider end_is_len
  31982                         // is only true if it equals the length WITHOUT the
  31983                         // sentinel, so we don't add a sentinel type.
  31984                         const slice_len_val = try pt.intValue(.usize, slice_len);
  31985                         if (end_val.eql(slice_len_val, .usize, zcu)) {
  31986                             end_is_len = true;
  31987                         }
  31988                     }
  31989                 }
  31990                 break :e end;
  31991             }
  31992             break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
  31993         }
  31994         if (!end_is_len) {
  31995             if (by_length) {
  31996                 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31997                 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  31998                 break :e try sema.coerce(block, .usize, uncasted_end, end_src);
  31999             } else break :e try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  32000         }
  32001 
  32002         // when slicing a many-item pointer, if a sentinel `S` is provided as in `ptr[a.. :S]`, it
  32003         // must match the sentinel of `@TypeOf(ptr)`.
  32004         sentinel_check: {
  32005             if (sentinel_opt == .none) break :sentinel_check;
  32006             const provided = provided: {
  32007                 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
  32008                 try checkSentinelType(sema, block, sentinel_src, elem_ty);
  32009                 break :provided try sema.resolveConstDefinedValue(
  32010                     block,
  32011                     sentinel_src,
  32012                     casted,
  32013                     .{ .simple = .slice_sentinel },
  32014                 );
  32015             };
  32016 
  32017             if (ptr_sentinel) |current| {
  32018                 if (provided.toIntern() == current.toIntern()) break :sentinel_check;
  32019             }
  32020 
  32021             return sema.failWithOwnedErrorMsg(block, msg: {
  32022                 const msg = try sema.errMsg(sentinel_src, "sentinel-terminated slicing of many-item pointer must match existing sentinel", .{});
  32023                 errdefer msg.destroy(sema.gpa);
  32024                 if (ptr_sentinel) |current| {
  32025                     try sema.errNote(sentinel_src, msg, "expected sentinel '{f}', found '{f}'", .{ current.fmtValue(pt), provided.fmtValue(pt) });
  32026                 } else {
  32027                     try sema.errNote(ptr_src, msg, "type '{f}' does not have a sentinel", .{slice_ty.fmt(pt)});
  32028                 }
  32029                 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{});
  32030                 break :msg msg;
  32031             });
  32032         }
  32033         return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
  32034     };
  32035 
  32036     const sentinel = s: {
  32037         if (sentinel_opt != .none) {
  32038             const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
  32039             try checkSentinelType(sema, block, sentinel_src, elem_ty);
  32040             break :s try sema.resolveConstDefinedValue(block, sentinel_src, casted, .{ .simple = .slice_sentinel });
  32041         }
  32042         // If we are slicing to the end of something that is sentinel-terminated
  32043         // then the resulting slice type is also sentinel-terminated.
  32044         if (end_is_len) {
  32045             if (ptr_sentinel) |sent| {
  32046                 break :s sent;
  32047             }
  32048         }
  32049         break :s null;
  32050     };
  32051     const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
  32052 
  32053     var checked_start_lte_end = by_length;
  32054     var runtime_src: ?LazySrcLoc = null;
  32055 
  32056     // requirement: start <= end
  32057     if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  32058         if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
  32059             if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, .usize))) {
  32060                 return sema.fail(
  32061                     block,
  32062                     start_src,
  32063                     "start index {f} is larger than end index {f}",
  32064                     .{
  32065                         start_val.fmtValueSema(pt, sema),
  32066                         end_val.fmtValueSema(pt, sema),
  32067                     },
  32068                 );
  32069             }
  32070             checked_start_lte_end = true;
  32071             if (try sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: {
  32072                 const expected_sentinel = sentinel orelse break :sentinel_check;
  32073                 const start_int = start_val.toUnsignedInt(zcu);
  32074                 const end_int = end_val.toUnsignedInt(zcu);
  32075                 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
  32076 
  32077                 const many_ptr_ty = try pt.manyConstPtrType(elem_ty);
  32078                 const many_ptr_val = try pt.getCoerced(ptr_val, many_ptr_ty);
  32079                 const elem_ptr = try many_ptr_val.ptrElem(sentinel_index, pt);
  32080                 const res = try sema.pointerDerefExtra(block, src, elem_ptr);
  32081                 const actual_sentinel = switch (res) {
  32082                     .runtime_load => break :sentinel_check,
  32083                     .val => |v| v,
  32084                     .needed_well_defined => |ty| return sema.fail(
  32085                         block,
  32086                         src,
  32087                         "comptime dereference requires '{f}' to have a well-defined layout",
  32088                         .{ty.fmt(pt)},
  32089                     ),
  32090                     .out_of_bounds => |ty| return sema.fail(
  32091                         block,
  32092                         end_src,
  32093                         "slice end index {d} exceeds bounds of containing decl of type '{f}'",
  32094                         .{ end_int, ty.fmt(pt) },
  32095                     ),
  32096                 };
  32097 
  32098                 if (!actual_sentinel.eql(expected_sentinel, elem_ty, zcu)) {
  32099                     const msg = msg: {
  32100                         const msg = try sema.errMsg(src, "value in memory does not match slice sentinel", .{});
  32101                         errdefer msg.destroy(sema.gpa);
  32102                         try sema.errNote(src, msg, "expected '{f}', found '{f}'", .{
  32103                             expected_sentinel.fmtValueSema(pt, sema),
  32104                             actual_sentinel.fmtValueSema(pt, sema),
  32105                         });
  32106 
  32107                         break :msg msg;
  32108                     };
  32109                     return sema.failWithOwnedErrorMsg(block, msg);
  32110                 }
  32111             } else {
  32112                 runtime_src = ptr_src;
  32113             }
  32114         } else {
  32115             runtime_src = start_src;
  32116         }
  32117     } else {
  32118         runtime_src = end_src;
  32119     }
  32120 
  32121     if (!checked_start_lte_end and block.wantSafety() and !block.isComptime()) {
  32122         // requirement: start <= end
  32123         assert(!block.isComptime());
  32124         try sema.requireRuntimeBlock(block, src, runtime_src.?);
  32125         const ok = try block.addBinOp(.cmp_lte, start, end);
  32126         try sema.addSafetyCheckCall(block, src, ok, .@"panic.startGreaterThanEnd", &.{ start, end });
  32127     }
  32128     const new_len = if (by_length)
  32129         try sema.coerce(block, .usize, uncasted_end_opt, end_src)
  32130     else
  32131         try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
  32132     const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
  32133 
  32134     const new_ptr_ty_info = new_ptr_ty.ptrInfo(zcu);
  32135     const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(zcu) != .c;
  32136 
  32137     if (opt_new_len_val) |new_len_val| {
  32138         const new_len_int = try new_len_val.toUnsignedIntSema(pt);
  32139 
  32140         const return_ty = try pt.ptrTypeSema(.{
  32141             .child = (try pt.arrayType(.{
  32142                 .len = new_len_int,
  32143                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  32144                 .child = elem_ty.toIntern(),
  32145             })).toIntern(),
  32146             .flags = .{
  32147                 .alignment = new_ptr_ty_info.flags.alignment,
  32148                 .is_const = new_ptr_ty_info.flags.is_const,
  32149                 .is_allowzero = new_allowzero,
  32150                 .is_volatile = new_ptr_ty_info.flags.is_volatile,
  32151                 .address_space = new_ptr_ty_info.flags.address_space,
  32152             },
  32153         });
  32154 
  32155         const opt_new_ptr_val = try sema.resolveValue(new_ptr);
  32156         const new_ptr_val = opt_new_ptr_val orelse {
  32157             const result = try block.addBitCast(return_ty, new_ptr);
  32158             if (block.wantSafety()) {
  32159                 // requirement: slicing C ptr is non-null
  32160                 if (ptr_ptr_child_ty.isCPtr(zcu)) {
  32161                     const is_non_null = try sema.analyzeIsNull(block, ptr, true);
  32162                     try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
  32163                 }
  32164 
  32165                 bounds_check: {
  32166                     const actual_len = if (array_ty.zigTypeTag(zcu) == .array)
  32167                         try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu))
  32168                     else if (slice_ty.isSlice(zcu)) l: {
  32169                         const slice_len = try sema.analyzeSliceLen(block, src, ptr_or_slice);
  32170                         break :l if (slice_ty.sentinel(zcu) == null)
  32171                             slice_len
  32172                         else
  32173                             try sema.analyzeArithmetic(block, .add, slice_len, .one, src, end_src, end_src, true);
  32174                     } else break :bounds_check;
  32175 
  32176                     const actual_end = if (slice_sentinel != null)
  32177                         try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  32178                     else
  32179                         end;
  32180 
  32181                     try sema.addSafetyCheckIndexOob(block, src, actual_end, actual_len, .cmp_lte);
  32182                 }
  32183 
  32184                 // requirement: result[new_len] == slice_sentinel
  32185                 try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
  32186             }
  32187             return result;
  32188         };
  32189 
  32190         if (!new_ptr_val.isUndef(zcu)) {
  32191             return Air.internedToRef((try pt.getCoerced(new_ptr_val, return_ty)).toIntern());
  32192         }
  32193 
  32194         // Special case: @as([]i32, undefined)[x..x]
  32195         if (new_len_int == 0) {
  32196             return pt.undefRef(return_ty);
  32197         }
  32198 
  32199         return sema.fail(block, src, "non-zero length slice of undefined pointer", .{});
  32200     }
  32201 
  32202     const return_ty = try pt.ptrTypeSema(.{
  32203         .child = elem_ty.toIntern(),
  32204         .sentinel = if (sentinel) |s| s.toIntern() else .none,
  32205         .flags = .{
  32206             .size = .slice,
  32207             .alignment = new_ptr_ty_info.flags.alignment,
  32208             .is_const = new_ptr_ty_info.flags.is_const,
  32209             .is_volatile = new_ptr_ty_info.flags.is_volatile,
  32210             .is_allowzero = new_allowzero,
  32211             .address_space = new_ptr_ty_info.flags.address_space,
  32212         },
  32213     });
  32214 
  32215     try sema.requireRuntimeBlock(block, src, runtime_src.?);
  32216     if (block.wantSafety()) {
  32217         // requirement: slicing C ptr is non-null
  32218         if (ptr_ptr_child_ty.isCPtr(zcu)) {
  32219             const is_non_null = try sema.analyzeIsNull(block, ptr, true);
  32220             try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
  32221         }
  32222 
  32223         // requirement: end <= len
  32224         const opt_len_inst = if (array_ty.zigTypeTag(zcu) == .array)
  32225             try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu))
  32226         else if (slice_ty.isSlice(zcu)) blk: {
  32227             if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
  32228                 // we don't need to add one for sentinels because the
  32229                 // underlying value data includes the sentinel
  32230                 break :blk try pt.intRef(.usize, try slice_val.sliceLen(pt));
  32231             }
  32232 
  32233             const slice_len_inst = try block.addTyOp(.slice_len, .usize, ptr_or_slice);
  32234             if (slice_ty.sentinel(zcu) == null) break :blk slice_len_inst;
  32235 
  32236             // we have to add one because slice lengths don't include the sentinel
  32237             break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
  32238         } else null;
  32239         if (opt_len_inst) |len_inst| {
  32240             const actual_end = if (slice_sentinel != null)
  32241                 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  32242             else
  32243                 end;
  32244             try sema.addSafetyCheckIndexOob(block, src, actual_end, len_inst, .cmp_lte);
  32245         }
  32246 
  32247         // requirement: start <= end
  32248         try sema.addSafetyCheckIndexOob(block, src, start, end, .cmp_lte);
  32249     }
  32250     const result = try block.addInst(.{
  32251         .tag = .slice,
  32252         .data = .{ .ty_pl = .{
  32253             .ty = Air.internedToRef(return_ty.toIntern()),
  32254             .payload = try sema.addExtra(Air.Bin{
  32255                 .lhs = new_ptr,
  32256                 .rhs = new_len,
  32257             }),
  32258         } },
  32259     });
  32260     if (block.wantSafety()) {
  32261         // requirement: result[new_len] == slice_sentinel
  32262         try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
  32263     }
  32264     return result;
  32265 }
  32266 
  32267 /// Asserts that lhs and rhs types are both numeric.
  32268 fn cmpNumeric(
  32269     sema: *Sema,
  32270     block: *Block,
  32271     src: LazySrcLoc,
  32272     uncasted_lhs: Air.Inst.Ref,
  32273     uncasted_rhs: Air.Inst.Ref,
  32274     op: std.math.CompareOperator,
  32275     lhs_src: LazySrcLoc,
  32276     rhs_src: LazySrcLoc,
  32277 ) CompileError!Air.Inst.Ref {
  32278     const pt = sema.pt;
  32279     const zcu = pt.zcu;
  32280     const lhs_ty = sema.typeOf(uncasted_lhs);
  32281     const rhs_ty = sema.typeOf(uncasted_rhs);
  32282 
  32283     assert(lhs_ty.isNumeric(zcu));
  32284     assert(rhs_ty.isNumeric(zcu));
  32285 
  32286     const lhs_ty_tag = lhs_ty.zigTypeTag(zcu);
  32287     const rhs_ty_tag = rhs_ty.zigTypeTag(zcu);
  32288     const target = zcu.getTarget();
  32289 
  32290     // One exception to heterogeneous comparison: comptime_float needs to
  32291     // coerce to fixed-width float.
  32292 
  32293     const lhs = if (lhs_ty_tag == .comptime_float and rhs_ty_tag == .float)
  32294         try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src)
  32295     else
  32296         uncasted_lhs;
  32297 
  32298     const rhs = if (lhs_ty_tag == .float and rhs_ty_tag == .comptime_float)
  32299         try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src)
  32300     else
  32301         uncasted_rhs;
  32302 
  32303     const maybe_lhs_val = try sema.resolveValue(lhs);
  32304     const maybe_rhs_val = try sema.resolveValue(rhs);
  32305 
  32306     // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS value.
  32307     if (maybe_lhs_val) |lhs_val| {
  32308         // Result based on comparison exceeding type bounds
  32309         if (!lhs_val.isUndef(zcu) and (lhs_ty_tag == .int or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) {
  32310             if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| {
  32311                 return if (res) .bool_true else .bool_false;
  32312             }
  32313         }
  32314         // Result based on NaN comparison
  32315         if (lhs_val.isNan(zcu)) {
  32316             return if (op == .neq) .bool_true else .bool_false;
  32317         }
  32318         // Result based on inf comparison to int
  32319         if (lhs_val.isInf(zcu) and rhs_ty_tag == .int) return switch (op) {
  32320             .neq => .bool_true,
  32321             .eq => .bool_false,
  32322             .gt, .gte => if (lhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
  32323             .lt, .lte => if (lhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
  32324         };
  32325     }
  32326 
  32327     // If the RHS is const, check if there is a guaranteed result which does not depend on ths LHS value.
  32328     if (maybe_rhs_val) |rhs_val| {
  32329         // Result based on comparison exceeding type bounds
  32330         if (!rhs_val.isUndef(zcu) and (rhs_ty_tag == .int or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {
  32331             if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| {
  32332                 return if (res) .bool_true else .bool_false;
  32333             }
  32334         }
  32335         // Result based on NaN comparison
  32336         if (rhs_val.isNan(zcu)) {
  32337             return if (op == .neq) .bool_true else .bool_false;
  32338         }
  32339         // Result based on inf comparison to int
  32340         if (rhs_val.isInf(zcu) and lhs_ty_tag == .int) return switch (op) {
  32341             .neq => .bool_true,
  32342             .eq => .bool_false,
  32343             .gt, .gte => if (rhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
  32344             .lt, .lte => if (rhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
  32345         };
  32346     }
  32347 
  32348     // Any other comparison depends on both values, so the result is undef if either is undef.
  32349     if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
  32350     if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
  32351 
  32352     const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: {
  32353         if (maybe_rhs_val) |rhs_val| {
  32354             const res = try Value.compareHeteroSema(lhs_val, op, rhs_val, pt);
  32355             return if (res) .bool_true else .bool_false;
  32356         } else break :rs rhs_src;
  32357     } else lhs_src;
  32358 
  32359     // TODO handle comparisons against lazy zero values
  32360     // Some values can be compared against zero without being runtime-known or without forcing
  32361     // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
  32362     // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
  32363     // of this function if we don't need to.
  32364     try sema.requireRuntimeBlock(block, src, runtime_src);
  32365 
  32366     // For floats, emit a float comparison instruction.
  32367     const lhs_is_float = switch (lhs_ty_tag) {
  32368         .float, .comptime_float => true,
  32369         else => false,
  32370     };
  32371     const rhs_is_float = switch (rhs_ty_tag) {
  32372         .float, .comptime_float => true,
  32373         else => false,
  32374     };
  32375 
  32376     if (lhs_is_float and rhs_is_float) {
  32377         // Smaller fixed-width floats coerce to larger fixed-width floats.
  32378         // comptime_float coerces to fixed-width float.
  32379         const dest_ty = x: {
  32380             if (lhs_ty_tag == .comptime_float) {
  32381                 break :x rhs_ty;
  32382             } else if (rhs_ty_tag == .comptime_float) {
  32383                 break :x lhs_ty;
  32384             }
  32385             if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) {
  32386                 break :x lhs_ty;
  32387             } else {
  32388                 break :x rhs_ty;
  32389             }
  32390         };
  32391         const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  32392         const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  32393         return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs);
  32394     }
  32395 
  32396     // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
  32397     // For mixed signed and unsigned integers, implicit cast both operands to a signed
  32398     // integer with + 1 bit.
  32399     // For mixed floats and integers, extract the integer part from the float, cast that to
  32400     // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
  32401     // add/subtract 1.
  32402     const lhs_is_signed = if (maybe_lhs_val) |lhs_val|
  32403         !(try lhs_val.compareAllWithZeroSema(.gte, pt))
  32404     else
  32405         (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu));
  32406     const rhs_is_signed = if (maybe_rhs_val) |rhs_val|
  32407         !(try rhs_val.compareAllWithZeroSema(.gte, pt))
  32408     else
  32409         (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu));
  32410     const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
  32411 
  32412     var dest_float_type: ?Type = null;
  32413 
  32414     var lhs_bits: usize = undefined;
  32415     if (maybe_lhs_val) |unresolved_lhs_val| {
  32416         const lhs_val = try sema.resolveLazyValue(unresolved_lhs_val);
  32417         if (!rhs_is_signed) {
  32418             switch (lhs_val.orderAgainstZero(zcu)) {
  32419                 .gt => {},
  32420                 .eq => switch (op) { // LHS = 0, RHS is unsigned
  32421                     .lte => return .bool_true,
  32422                     .gt => return .bool_false,
  32423                     else => {},
  32424                 },
  32425                 .lt => switch (op) { // LHS < 0, RHS is unsigned
  32426                     .neq, .lt, .lte => return .bool_true,
  32427                     .eq, .gt, .gte => return .bool_false,
  32428                 },
  32429             }
  32430         }
  32431         if (lhs_is_float) {
  32432             const float = lhs_val.toFloat(f128, zcu);
  32433             var big_int: std.math.big.int.Mutable = .{
  32434                 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)),
  32435                 .len = undefined,
  32436                 .positive = undefined,
  32437             };
  32438             switch (big_int.setFloat(float, .away)) {
  32439                 .inexact => switch (op) {
  32440                     .eq => return .bool_false,
  32441                     .neq => return .bool_true,
  32442                     else => {},
  32443                 },
  32444                 .exact => {},
  32445             }
  32446             lhs_bits = big_int.toConst().bitCountTwosComp();
  32447         } else {
  32448             lhs_bits = lhs_val.intBitCountTwosComp(zcu);
  32449         }
  32450         lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed);
  32451     } else if (lhs_is_float) {
  32452         dest_float_type = lhs_ty;
  32453     } else {
  32454         const int_info = lhs_ty.intInfo(zcu);
  32455         lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  32456     }
  32457 
  32458     var rhs_bits: usize = undefined;
  32459     if (maybe_rhs_val) |unresolved_rhs_val| {
  32460         const rhs_val = try sema.resolveLazyValue(unresolved_rhs_val);
  32461         if (!lhs_is_signed) {
  32462             switch (rhs_val.orderAgainstZero(zcu)) {
  32463                 .gt => {},
  32464                 .eq => switch (op) { // RHS = 0, LHS is unsigned
  32465                     .gte => return .bool_true,
  32466                     .lt => return .bool_false,
  32467                     else => {},
  32468                 },
  32469                 .lt => switch (op) { // RHS < 0, LHS is unsigned
  32470                     .neq, .gt, .gte => return .bool_true,
  32471                     .eq, .lt, .lte => return .bool_false,
  32472                 },
  32473             }
  32474         }
  32475         if (rhs_is_float) {
  32476             const float = rhs_val.toFloat(f128, zcu);
  32477             var big_int: std.math.big.int.Mutable = .{
  32478                 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)),
  32479                 .len = undefined,
  32480                 .positive = undefined,
  32481             };
  32482             switch (big_int.setFloat(float, .away)) {
  32483                 .inexact => switch (op) {
  32484                     .eq => return .bool_false,
  32485                     .neq => return .bool_true,
  32486                     else => {},
  32487                 },
  32488                 .exact => {},
  32489             }
  32490             rhs_bits = big_int.toConst().bitCountTwosComp();
  32491         } else {
  32492             rhs_bits = rhs_val.intBitCountTwosComp(zcu);
  32493         }
  32494         rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed);
  32495     } else if (rhs_is_float) {
  32496         dest_float_type = rhs_ty;
  32497     } else {
  32498         const int_info = rhs_ty.intInfo(zcu);
  32499         rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  32500     }
  32501 
  32502     const dest_ty = if (dest_float_type) |ft| ft else blk: {
  32503         const max_bits = @max(lhs_bits, rhs_bits);
  32504         const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits});
  32505         const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
  32506         break :blk try pt.intType(signedness, casted_bits);
  32507     };
  32508     const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  32509     const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  32510 
  32511     return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs);
  32512 }
  32513 
  32514 /// Asserts that LHS value is an int or comptime int and not undefined, and
  32515 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to
  32516 /// determine whether `op` has a guaranteed result.
  32517 /// If it cannot be determined, returns null.
  32518 /// Otherwise returns a bool for the guaranteed comparison operation.
  32519 fn compareIntsOnlyPossibleResult(
  32520     sema: *Sema,
  32521     lhs_val: Value,
  32522     op: std.math.CompareOperator,
  32523     rhs_ty: Type,
  32524 ) SemaError!?bool {
  32525     const pt = sema.pt;
  32526     const zcu = pt.zcu;
  32527 
  32528     const min_rhs = try rhs_ty.minInt(pt, rhs_ty);
  32529     const max_rhs = try rhs_ty.maxInt(pt, rhs_ty);
  32530 
  32531     if (min_rhs.toIntern() == max_rhs.toIntern()) {
  32532         // RHS is effectively comptime-known.
  32533         return try Value.compareHeteroSema(lhs_val, op, min_rhs, pt);
  32534     }
  32535 
  32536     const against_min = try lhs_val.orderAdvanced(min_rhs, .sema, zcu, pt.tid);
  32537     const against_max = try lhs_val.orderAdvanced(max_rhs, .sema, zcu, pt.tid);
  32538 
  32539     switch (op) {
  32540         .eq => {
  32541             if (against_min.compare(.lt)) return false;
  32542             if (against_max.compare(.gt)) return false;
  32543         },
  32544         .neq => {
  32545             if (against_min.compare(.lt)) return true;
  32546             if (against_max.compare(.gt)) return true;
  32547         },
  32548         .lt => {
  32549             if (against_min.compare(.lt)) return true;
  32550             if (against_max.compare(.gte)) return false;
  32551         },
  32552         .gt => {
  32553             if (against_max.compare(.gt)) return true;
  32554             if (against_min.compare(.lte)) return false;
  32555         },
  32556         .lte => {
  32557             if (against_min.compare(.lte)) return true;
  32558             if (against_max.compare(.gt)) return false;
  32559         },
  32560         .gte => {
  32561             if (against_max.compare(.gte)) return true;
  32562             if (against_min.compare(.lt)) return false;
  32563         },
  32564     }
  32565 
  32566     return null;
  32567 }
  32568 
  32569 /// Asserts that lhs and rhs types are both vectors.
  32570 fn cmpVector(
  32571     sema: *Sema,
  32572     block: *Block,
  32573     src: LazySrcLoc,
  32574     lhs: Air.Inst.Ref,
  32575     rhs: Air.Inst.Ref,
  32576     op: std.math.CompareOperator,
  32577     lhs_src: LazySrcLoc,
  32578     rhs_src: LazySrcLoc,
  32579 ) CompileError!Air.Inst.Ref {
  32580     const pt = sema.pt;
  32581     const zcu = pt.zcu;
  32582     const lhs_ty = sema.typeOf(lhs);
  32583     const rhs_ty = sema.typeOf(rhs);
  32584     assert(lhs_ty.zigTypeTag(zcu) == .vector);
  32585     assert(rhs_ty.zigTypeTag(zcu) == .vector);
  32586     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  32587 
  32588     const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } });
  32589     const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src);
  32590     const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src);
  32591 
  32592     const result_ty = try pt.vectorType(.{
  32593         .len = lhs_ty.vectorLen(zcu),
  32594         .child = .bool_type,
  32595     });
  32596 
  32597     const maybe_lhs_val = try sema.resolveValue(casted_lhs);
  32598     const maybe_rhs_val = try sema.resolveValue(casted_rhs);
  32599     if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
  32600     if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
  32601 
  32602     const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: {
  32603         if (maybe_rhs_val) |rhs_val| {
  32604             const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
  32605             return Air.internedToRef(cmp_val.toIntern());
  32606         } else break :src rhs_src;
  32607     } else lhs_src;
  32608 
  32609     try sema.requireRuntimeBlock(block, src, runtime_src);
  32610     return block.addCmpVector(casted_lhs, casted_rhs, op);
  32611 }
  32612 
  32613 fn wrapOptional(
  32614     sema: *Sema,
  32615     block: *Block,
  32616     dest_ty: Type,
  32617     inst: Air.Inst.Ref,
  32618     inst_src: LazySrcLoc,
  32619 ) !Air.Inst.Ref {
  32620     if (try sema.resolveValue(inst)) |val| {
  32621         return Air.internedToRef((try sema.pt.intern(.{ .opt = .{
  32622             .ty = dest_ty.toIntern(),
  32623             .val = val.toIntern(),
  32624         } })));
  32625     }
  32626 
  32627     try sema.requireRuntimeBlock(block, inst_src, null);
  32628     return block.addTyOp(.wrap_optional, dest_ty, inst);
  32629 }
  32630 
  32631 fn wrapErrorUnionPayload(
  32632     sema: *Sema,
  32633     block: *Block,
  32634     dest_ty: Type,
  32635     inst: Air.Inst.Ref,
  32636     inst_src: LazySrcLoc,
  32637 ) !Air.Inst.Ref {
  32638     const pt = sema.pt;
  32639     const zcu = pt.zcu;
  32640     const dest_payload_ty = dest_ty.errorUnionPayload(zcu);
  32641     const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false });
  32642     if (try sema.resolveValue(coerced)) |val| {
  32643         return Air.internedToRef((try pt.intern(.{ .error_union = .{
  32644             .ty = dest_ty.toIntern(),
  32645             .val = .{ .payload = val.toIntern() },
  32646         } })));
  32647     }
  32648     try sema.requireRuntimeBlock(block, inst_src, null);
  32649     return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
  32650 }
  32651 
  32652 fn wrapErrorUnionSet(
  32653     sema: *Sema,
  32654     block: *Block,
  32655     dest_ty: Type,
  32656     inst: Air.Inst.Ref,
  32657     inst_src: LazySrcLoc,
  32658 ) !Air.Inst.Ref {
  32659     const pt = sema.pt;
  32660     const zcu = pt.zcu;
  32661     const ip = &zcu.intern_pool;
  32662     const inst_ty = sema.typeOf(inst);
  32663     const dest_err_set_ty = dest_ty.errorUnionSet(zcu);
  32664     if (try sema.resolveValue(inst)) |val| {
  32665         const expected_name = zcu.intern_pool.indexToKey(val.toIntern()).err.name;
  32666         switch (dest_err_set_ty.toIntern()) {
  32667             .anyerror_type => {},
  32668             .adhoc_inferred_error_set_type => ok: {
  32669                 const ies = sema.fn_ret_ty_ies.?;
  32670                 switch (ies.resolved) {
  32671                     .anyerror_type => break :ok,
  32672                     .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) {
  32673                         break :ok;
  32674                     },
  32675                     else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) {
  32676                         break :ok;
  32677                     },
  32678                 }
  32679                 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  32680             },
  32681             else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) {
  32682                 .error_set_type => |error_set_type| ok: {
  32683                     if (error_set_type.nameIndex(ip, expected_name) != null) break :ok;
  32684                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  32685                 },
  32686                 .inferred_error_set_type => |func_index| ok: {
  32687                     // We carefully do this in an order that avoids unnecessarily
  32688                     // resolving the destination error set type.
  32689                     try zcu.maybeUnresolveIes(func_index);
  32690                     switch (ip.funcIesResolvedUnordered(func_index)) {
  32691                         .anyerror_type => break :ok,
  32692                         .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) {
  32693                             break :ok;
  32694                         },
  32695                         else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) {
  32696                             break :ok;
  32697                         },
  32698                     }
  32699 
  32700                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  32701                 },
  32702                 else => unreachable,
  32703             },
  32704         }
  32705         return Air.internedToRef((try pt.intern(.{ .error_union = .{
  32706             .ty = dest_ty.toIntern(),
  32707             .val = .{ .err_name = expected_name },
  32708         } })));
  32709     }
  32710 
  32711     try sema.requireRuntimeBlock(block, inst_src, null);
  32712     const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);
  32713     return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
  32714 }
  32715 
  32716 fn unionToTag(
  32717     sema: *Sema,
  32718     block: *Block,
  32719     enum_ty: Type,
  32720     un: Air.Inst.Ref,
  32721     un_src: LazySrcLoc,
  32722 ) !Air.Inst.Ref {
  32723     const pt = sema.pt;
  32724     const zcu = pt.zcu;
  32725     if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| {
  32726         return Air.internedToRef(opv.toIntern());
  32727     }
  32728     if (try sema.resolveValue(un)) |un_val| {
  32729         const tag_val = un_val.unionTag(zcu).?;
  32730         if (tag_val.isUndef(zcu))
  32731             return try pt.undefRef(enum_ty);
  32732         return Air.internedToRef(tag_val.toIntern());
  32733     }
  32734     try sema.requireRuntimeBlock(block, un_src, null);
  32735     return block.addTyOp(.get_union_tag, enum_ty, un);
  32736 }
  32737 
  32738 const PeerResolveStrategy = enum {
  32739     /// The type is not known.
  32740     /// If refined no further, this is equivalent to `exact`.
  32741     unknown,
  32742     /// The type may be an error set or error union.
  32743     /// If refined no further, it is an error set.
  32744     error_set,
  32745     /// The type must be some error union.
  32746     error_union,
  32747     /// The type may be @TypeOf(null), an optional or a C pointer.
  32748     /// If refined no further, it is @TypeOf(null).
  32749     nullable,
  32750     /// The type must be some optional or a C pointer.
  32751     /// If refined no further, it is an optional.
  32752     optional,
  32753     /// The type must be either an array or a vector.
  32754     /// If refined no further, it is an array.
  32755     array,
  32756     /// The type must be a vector.
  32757     vector,
  32758     /// The type must be a C pointer.
  32759     c_ptr,
  32760     /// The type must be a pointer (C or not).
  32761     /// If refined no further, it is a non-C pointer.
  32762     ptr,
  32763     /// The type must be a function or a pointer to a function.
  32764     /// If refined no further, it is a function.
  32765     func,
  32766     /// The type must be an enum literal, or some specific enum or union. Which one is decided
  32767     /// afterwards based on the types in question.
  32768     enum_or_union,
  32769     /// The type must be some integer or float type.
  32770     /// If refined no further, it is `comptime_int`.
  32771     comptime_int,
  32772     /// The type must be some float type.
  32773     /// If refined no further, it is `comptime_float`.
  32774     comptime_float,
  32775     /// The type must be some float or fixed-width integer type.
  32776     /// If refined no further, it is some fixed-width integer type.
  32777     fixed_int,
  32778     /// The type must be some fixed-width float type.
  32779     fixed_float,
  32780     /// The type must be a tuple.
  32781     tuple,
  32782     /// The peers must all be of the same type.
  32783     exact,
  32784 
  32785     /// Given two strategies, find a strategy that satisfies both, if one exists. If no such
  32786     /// strategy exists, any strategy may be returned; an error will be emitted when the caller
  32787     /// attempts to use the strategy to resolve the type.
  32788     /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at
  32789     /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy.
  32790     fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy {
  32791         // Our merging should be order-independent. Thus, even though the union order is arbitrary,
  32792         // by sorting the tags and switching first on the smaller, we have half as many cases to
  32793         // worry about (since we avoid the duplicates).
  32794         const s0_is_a = @intFromEnum(a) <= @intFromEnum(b);
  32795         const s0 = if (s0_is_a) a else b;
  32796         const s1 = if (s0_is_a) b else a;
  32797 
  32798         const ReasonMethod = enum {
  32799             all_s0,
  32800             all_s1,
  32801             either,
  32802         };
  32803 
  32804         const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) {
  32805             .unknown => .{ .all_s1, s1 },
  32806             .error_set => switch (s1) {
  32807                 .error_set => .{ .either, .error_set },
  32808                 else => .{ .all_s0, .error_union },
  32809             },
  32810             .error_union => switch (s1) {
  32811                 .error_union => .{ .either, .error_union },
  32812                 else => .{ .all_s0, .error_union },
  32813             },
  32814             .nullable => switch (s1) {
  32815                 .nullable => .{ .either, .nullable },
  32816                 .c_ptr => .{ .all_s1, .c_ptr },
  32817                 else => .{ .all_s0, .optional },
  32818             },
  32819             .optional => switch (s1) {
  32820                 .optional => .{ .either, .optional },
  32821                 .c_ptr => .{ .all_s1, .c_ptr },
  32822                 else => .{ .all_s0, .optional },
  32823             },
  32824             .array => switch (s1) {
  32825                 .array => .{ .either, .array },
  32826                 .vector => .{ .all_s1, .vector },
  32827                 else => .{ .all_s0, .array },
  32828             },
  32829             .vector => switch (s1) {
  32830                 .vector => .{ .either, .vector },
  32831                 else => .{ .all_s0, .vector },
  32832             },
  32833             .c_ptr => switch (s1) {
  32834                 .c_ptr => .{ .either, .c_ptr },
  32835                 else => .{ .all_s0, .c_ptr },
  32836             },
  32837             .ptr => switch (s1) {
  32838                 .ptr => .{ .either, .ptr },
  32839                 else => .{ .all_s0, .ptr },
  32840             },
  32841             .func => switch (s1) {
  32842                 .func => .{ .either, .func },
  32843                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32844             },
  32845             .enum_or_union => switch (s1) {
  32846                 .enum_or_union => .{ .either, .enum_or_union },
  32847                 else => .{ .all_s0, .enum_or_union },
  32848             },
  32849             .comptime_int => switch (s1) {
  32850                 .comptime_int => .{ .either, .comptime_int },
  32851                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32852             },
  32853             .comptime_float => switch (s1) {
  32854                 .comptime_float => .{ .either, .comptime_float },
  32855                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32856             },
  32857             .fixed_int => switch (s1) {
  32858                 .fixed_int => .{ .either, .fixed_int },
  32859                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32860             },
  32861             .fixed_float => switch (s1) {
  32862                 .fixed_float => .{ .either, .fixed_float },
  32863                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32864             },
  32865             .tuple => switch (s1) {
  32866                 .exact => .{ .all_s1, .exact },
  32867                 else => .{ .all_s0, .tuple },
  32868             },
  32869             .exact => .{ .all_s0, .exact },
  32870         };
  32871 
  32872         switch (reason_method) {
  32873             .all_s0 => {
  32874                 if (!s0_is_a) {
  32875                     reason_peer.* = b_peer_idx;
  32876                 }
  32877             },
  32878             .all_s1 => {
  32879                 if (s0_is_a) {
  32880                     reason_peer.* = b_peer_idx;
  32881                 }
  32882             },
  32883             .either => {
  32884                 // Prefer the earliest peer
  32885                 reason_peer.* = @min(reason_peer.*, b_peer_idx);
  32886             },
  32887         }
  32888 
  32889         return strat;
  32890     }
  32891 
  32892     fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy {
  32893         return switch (ty.zigTypeTag(zcu)) {
  32894             .type, .void, .bool, .@"opaque", .frame, .@"anyframe" => .exact,
  32895             .noreturn, .undefined => .unknown,
  32896             .null => .nullable,
  32897             .comptime_int => .comptime_int,
  32898             .int => .fixed_int,
  32899             .comptime_float => .comptime_float,
  32900             .float => .fixed_float,
  32901             .pointer => if (ty.ptrInfo(zcu).flags.size == .c) .c_ptr else .ptr,
  32902             .array => .array,
  32903             .vector => .vector,
  32904             .optional => .optional,
  32905             .error_set => .error_set,
  32906             .error_union => .error_union,
  32907             .enum_literal, .@"enum", .@"union" => .enum_or_union,
  32908             .@"struct" => if (ty.isTuple(zcu)) .tuple else .exact,
  32909             .@"fn" => .func,
  32910         };
  32911     }
  32912 };
  32913 
  32914 const PeerTypeCandidateSrc = union(enum) {
  32915     /// Do not print out error notes for candidate sources
  32916     none: void,
  32917     /// When we want to know the the src of candidate i, look up at
  32918     /// index i in this slice
  32919     override: []const ?LazySrcLoc,
  32920     /// resolvePeerTypes originates from a @TypeOf(...) call
  32921     typeof_builtin_call_node_offset: std.zig.Ast.Node.Offset,
  32922 
  32923     pub fn resolve(
  32924         self: PeerTypeCandidateSrc,
  32925         block: *Block,
  32926         candidate_i: usize,
  32927     ) ?LazySrcLoc {
  32928         return switch (self) {
  32929             .none => null,
  32930             .override => |candidate_srcs| if (candidate_i >= candidate_srcs.len)
  32931                 null
  32932             else
  32933                 candidate_srcs[candidate_i],
  32934             .typeof_builtin_call_node_offset => |node_offset| block.builtinCallArgSrc(node_offset, @intCast(candidate_i)),
  32935         };
  32936     }
  32937 };
  32938 
  32939 const PeerResolveResult = union(enum) {
  32940     /// The peer type resolution was successful, and resulted in the given type.
  32941     success: Type,
  32942     /// There was some generic conflict between two peers.
  32943     conflict: struct {
  32944         peer_idx_a: usize,
  32945         peer_idx_b: usize,
  32946     },
  32947     /// There was an error when resolving the type of a struct or tuple field.
  32948     field_error: struct {
  32949         /// The name of the field which caused the failure.
  32950         field_name: InternPool.NullTerminatedString,
  32951         /// The type of this field in each peer.
  32952         field_types: []Type,
  32953         /// The error from resolving the field type. Guaranteed not to be `success`.
  32954         sub_result: *PeerResolveResult,
  32955     },
  32956 
  32957     fn report(
  32958         result: PeerResolveResult,
  32959         sema: *Sema,
  32960         block: *Block,
  32961         src: LazySrcLoc,
  32962         instructions: []const Air.Inst.Ref,
  32963         candidate_srcs: PeerTypeCandidateSrc,
  32964     ) !*Zcu.ErrorMsg {
  32965         const pt = sema.pt;
  32966 
  32967         var opt_msg: ?*Zcu.ErrorMsg = null;
  32968         errdefer if (opt_msg) |msg| msg.destroy(sema.gpa);
  32969 
  32970         // If we mention fields we'll want to include field types, so put peer types in a buffer
  32971         var peer_tys = try sema.arena.alloc(Type, instructions.len);
  32972         for (peer_tys, instructions) |*ty, inst| {
  32973             ty.* = sema.typeOf(inst);
  32974         }
  32975 
  32976         var cur = result;
  32977         while (true) {
  32978             var conflict_idx: [2]usize = undefined;
  32979 
  32980             switch (cur) {
  32981                 .success => unreachable,
  32982                 .conflict => |conflict| {
  32983                     // Fall through to two-peer conflict handling below
  32984                     conflict_idx = .{
  32985                         conflict.peer_idx_a,
  32986                         conflict.peer_idx_b,
  32987                     };
  32988                 },
  32989                 .field_error => |field_error| {
  32990                     const fmt = "struct field '{f}' has conflicting types";
  32991                     const args = .{field_error.field_name.fmt(&pt.zcu.intern_pool)};
  32992                     if (opt_msg) |msg| {
  32993                         try sema.errNote(src, msg, fmt, args);
  32994                     } else {
  32995                         opt_msg = try sema.errMsg(src, fmt, args);
  32996                     }
  32997 
  32998                     // Continue on to child error
  32999                     cur = field_error.sub_result.*;
  33000                     peer_tys = field_error.field_types;
  33001                     continue;
  33002                 },
  33003             }
  33004 
  33005             // This is the path for reporting a generic conflict between two peers.
  33006 
  33007             if (conflict_idx[1] < conflict_idx[0]) {
  33008                 // b comes first in source, so it's better if it comes first in the error
  33009                 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]);
  33010             }
  33011 
  33012             const conflict_tys: [2]Type = .{
  33013                 peer_tys[conflict_idx[0]],
  33014                 peer_tys[conflict_idx[1]],
  33015             };
  33016             const conflict_srcs: [2]?LazySrcLoc = .{
  33017                 candidate_srcs.resolve(block, conflict_idx[0]),
  33018                 candidate_srcs.resolve(block, conflict_idx[1]),
  33019             };
  33020 
  33021             const fmt = "incompatible types: '{f}' and '{f}'";
  33022             const args = .{
  33023                 conflict_tys[0].fmt(pt),
  33024                 conflict_tys[1].fmt(pt),
  33025             };
  33026             const msg = if (opt_msg) |msg| msg: {
  33027                 try sema.errNote(src, msg, fmt, args);
  33028                 break :msg msg;
  33029             } else msg: {
  33030                 const msg = try sema.errMsg(src, fmt, args);
  33031                 opt_msg = msg;
  33032                 break :msg msg;
  33033             };
  33034 
  33035             if (conflict_srcs[0]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[0].fmt(pt)});
  33036             if (conflict_srcs[1]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[1].fmt(pt)});
  33037 
  33038             // No child error
  33039             break;
  33040         }
  33041 
  33042         return opt_msg.?;
  33043     }
  33044 };
  33045 
  33046 fn resolvePeerTypes(
  33047     sema: *Sema,
  33048     block: *Block,
  33049     src: LazySrcLoc,
  33050     instructions: []const Air.Inst.Ref,
  33051     candidate_srcs: PeerTypeCandidateSrc,
  33052 ) !Type {
  33053     switch (instructions.len) {
  33054         0 => return .noreturn,
  33055         1 => return sema.typeOf(instructions[0]),
  33056         else => {},
  33057     }
  33058 
  33059     // Fast path: check if everything has the same type to bypass the main PTR logic.
  33060     same_type: {
  33061         const ty = sema.typeOf(instructions[0]);
  33062         for (instructions[1..]) |inst| {
  33063             if (sema.typeOf(inst).toIntern() != ty.toIntern()) {
  33064                 break :same_type;
  33065             }
  33066         }
  33067         return ty;
  33068     }
  33069 
  33070     const peer_tys = try sema.arena.alloc(?Type, instructions.len);
  33071     const peer_vals = try sema.arena.alloc(?Value, instructions.len);
  33072 
  33073     for (instructions, peer_tys, peer_vals) |inst, *ty, *val| {
  33074         ty.* = sema.typeOf(inst);
  33075         val.* = try sema.resolveValue(inst);
  33076     }
  33077 
  33078     switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) {
  33079         .success => |ty| return ty,
  33080         else => |result| {
  33081             const msg = try result.report(sema, block, src, instructions, candidate_srcs);
  33082             return sema.failWithOwnedErrorMsg(block, msg);
  33083         },
  33084     }
  33085 }
  33086 
  33087 fn resolvePeerTypesInner(
  33088     sema: *Sema,
  33089     block: *Block,
  33090     src: LazySrcLoc,
  33091     peer_tys: []?Type,
  33092     peer_vals: []?Value,
  33093 ) !PeerResolveResult {
  33094     const pt = sema.pt;
  33095     const zcu = pt.zcu;
  33096     const ip = &zcu.intern_pool;
  33097 
  33098     var strat_reason: usize = 0;
  33099     var s: PeerResolveStrategy = .unknown;
  33100     for (peer_tys, 0..) |opt_ty, i| {
  33101         const ty = opt_ty orelse continue;
  33102         s = s.merge(PeerResolveStrategy.select(ty, zcu), &strat_reason, i);
  33103     }
  33104 
  33105     if (s == .unknown) {
  33106         // The whole thing was noreturn or undefined - try to do an exact match
  33107         s = .exact;
  33108     } else {
  33109         // There was something other than noreturn and undefined, so we can ignore those peers
  33110         for (peer_tys) |*ty_ptr| {
  33111             const ty = ty_ptr.* orelse continue;
  33112             switch (ty.zigTypeTag(zcu)) {
  33113                 .noreturn, .undefined => ty_ptr.* = null,
  33114                 else => {},
  33115             }
  33116         }
  33117     }
  33118 
  33119     const target = zcu.getTarget();
  33120 
  33121     switch (s) {
  33122         .unknown => unreachable,
  33123 
  33124         .error_set => {
  33125             var final_set: ?Type = null;
  33126             for (peer_tys, 0..) |opt_ty, i| {
  33127                 const ty = opt_ty orelse continue;
  33128                 if (ty.zigTypeTag(zcu) != .error_set) return .{ .conflict = .{
  33129                     .peer_idx_a = strat_reason,
  33130                     .peer_idx_b = i,
  33131                 } };
  33132                 if (final_set) |cur_set| {
  33133                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty);
  33134                 } else {
  33135                     final_set = ty;
  33136                 }
  33137             }
  33138             return .{ .success = final_set.? };
  33139         },
  33140 
  33141         .error_union => {
  33142             var final_set: ?Type = null;
  33143             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  33144                 const ty = ty_ptr.* orelse continue;
  33145                 const set_ty = switch (ty.zigTypeTag(zcu)) {
  33146                     .error_set => blk: {
  33147                         ty_ptr.* = null; // no payload to decide on
  33148                         val_ptr.* = null;
  33149                         break :blk ty;
  33150                     },
  33151                     .error_union => blk: {
  33152                         const set_ty = ty.errorUnionSet(zcu);
  33153                         ty_ptr.* = ty.errorUnionPayload(zcu);
  33154                         if (val_ptr.*) |eu_val| switch (ip.indexToKey(eu_val.toIntern())) {
  33155                             .error_union => |eu| switch (eu.val) {
  33156                                 .payload => |payload_ip| val_ptr.* = Value.fromInterned(payload_ip),
  33157                                 .err_name => val_ptr.* = null,
  33158                             },
  33159                             .undef => val_ptr.* = Value.fromInterned(try pt.intern(.{ .undef = ty_ptr.*.?.toIntern() })),
  33160                             else => unreachable,
  33161                         };
  33162                         break :blk set_ty;
  33163                     },
  33164                     else => continue, // whole type is the payload
  33165                 };
  33166                 if (final_set) |cur_set| {
  33167                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty);
  33168                 } else {
  33169                     final_set = set_ty;
  33170                 }
  33171             }
  33172             assert(final_set != null);
  33173             const final_payload = switch (try sema.resolvePeerTypesInner(
  33174                 block,
  33175                 src,
  33176                 peer_tys,
  33177                 peer_vals,
  33178             )) {
  33179                 .success => |ty| ty,
  33180                 else => |result| return result,
  33181             };
  33182             return .{ .success = try pt.errorUnionType(final_set.?, final_payload) };
  33183         },
  33184 
  33185         .nullable => {
  33186             for (peer_tys, 0..) |opt_ty, i| {
  33187                 const ty = opt_ty orelse continue;
  33188                 if (!ty.eql(.null, zcu)) return .{ .conflict = .{
  33189                     .peer_idx_a = strat_reason,
  33190                     .peer_idx_b = i,
  33191                 } };
  33192             }
  33193             return .{ .success = .null };
  33194         },
  33195 
  33196         .optional => {
  33197             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  33198                 const ty = ty_ptr.* orelse continue;
  33199                 switch (ty.zigTypeTag(zcu)) {
  33200                     .null => {
  33201                         ty_ptr.* = null;
  33202                         val_ptr.* = null;
  33203                     },
  33204                     .optional => {
  33205                         ty_ptr.* = ty.optionalChild(zcu);
  33206                         if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(zcu)) opt_val.optionalValue(zcu) else null;
  33207                     },
  33208                     else => {},
  33209                 }
  33210             }
  33211             const child_ty = switch (try sema.resolvePeerTypesInner(
  33212                 block,
  33213                 src,
  33214                 peer_tys,
  33215                 peer_vals,
  33216             )) {
  33217                 .success => |ty| ty,
  33218                 else => |result| return result,
  33219             };
  33220             return .{ .success = try pt.optionalType(child_ty.toIntern()) };
  33221         },
  33222 
  33223         .array => {
  33224             // Index of the first non-null peer
  33225             var opt_first_idx: ?usize = null;
  33226             // Index of the first array or vector peer (i.e. not a tuple)
  33227             var opt_first_arr_idx: ?usize = null;
  33228             // Set to non-null once we see any peer, even a tuple
  33229             var len: u64 = undefined;
  33230             var sentinel: ?Value = undefined;
  33231             // Only set once we see a non-tuple peer
  33232             var elem_ty: Type = undefined;
  33233 
  33234             for (peer_tys, 0..) |*ty_ptr, i| {
  33235                 const ty = ty_ptr.* orelse continue;
  33236 
  33237                 if (!ty.isArrayOrVector(zcu)) {
  33238                     // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced.
  33239                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  33240                         .peer_idx_a = strat_reason,
  33241                         .peer_idx_b = i,
  33242                     } };
  33243 
  33244                     if (opt_first_idx) |first_idx| {
  33245                         if (arr_like.len != len) return .{ .conflict = .{
  33246                             .peer_idx_a = first_idx,
  33247                             .peer_idx_b = i,
  33248                         } };
  33249                     } else {
  33250                         opt_first_idx = i;
  33251                         len = arr_like.len;
  33252                     }
  33253 
  33254                     sentinel = null;
  33255 
  33256                     continue;
  33257                 }
  33258 
  33259                 const first_arr_idx = opt_first_arr_idx orelse {
  33260                     if (opt_first_idx == null) {
  33261                         opt_first_idx = i;
  33262                         len = ty.arrayLen(zcu);
  33263                         sentinel = ty.sentinel(zcu);
  33264                     }
  33265                     opt_first_arr_idx = i;
  33266                     elem_ty = ty.childType(zcu);
  33267                     continue;
  33268                 };
  33269 
  33270                 if (ty.arrayLen(zcu) != len) return .{ .conflict = .{
  33271                     .peer_idx_a = first_arr_idx,
  33272                     .peer_idx_b = i,
  33273                 } };
  33274 
  33275                 const peer_elem_ty = ty.childType(zcu);
  33276                 if (!peer_elem_ty.eql(elem_ty, zcu)) coerce: {
  33277                     const peer_elem_coerces_to_elem =
  33278                         try sema.coerceInMemoryAllowed(block, elem_ty, peer_elem_ty, false, zcu.getTarget(), src, src, null);
  33279                     if (peer_elem_coerces_to_elem == .ok) {
  33280                         break :coerce;
  33281                     }
  33282 
  33283                     const elem_coerces_to_peer_elem =
  33284                         try sema.coerceInMemoryAllowed(block, peer_elem_ty, elem_ty, false, zcu.getTarget(), src, src, null);
  33285                     if (elem_coerces_to_peer_elem == .ok) {
  33286                         elem_ty = peer_elem_ty;
  33287                         break :coerce;
  33288                     }
  33289 
  33290                     return .{ .conflict = .{
  33291                         .peer_idx_a = first_arr_idx,
  33292                         .peer_idx_b = i,
  33293                     } };
  33294                 }
  33295 
  33296                 if (sentinel) |cur_sent| {
  33297                     if (ty.sentinel(zcu)) |peer_sent| {
  33298                         if (!peer_sent.eql(cur_sent, elem_ty, zcu)) sentinel = null;
  33299                     } else {
  33300                         sentinel = null;
  33301                     }
  33302                 }
  33303             }
  33304 
  33305             // There should always be at least one array or vector peer
  33306             assert(opt_first_arr_idx != null);
  33307 
  33308             return .{ .success = try pt.arrayType(.{
  33309                 .len = len,
  33310                 .child = elem_ty.toIntern(),
  33311                 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none,
  33312             }) };
  33313         },
  33314 
  33315         .vector => {
  33316             var len: ?u64 = null;
  33317             var first_idx: usize = undefined;
  33318             for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| {
  33319                 const ty = ty_ptr.* orelse continue;
  33320 
  33321                 if (!ty.isArrayOrVector(zcu)) {
  33322                     // Allow tuples of the correct length
  33323                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  33324                         .peer_idx_a = strat_reason,
  33325                         .peer_idx_b = i,
  33326                     } };
  33327 
  33328                     if (len) |expect_len| {
  33329                         if (arr_like.len != expect_len) return .{ .conflict = .{
  33330                             .peer_idx_a = first_idx,
  33331                             .peer_idx_b = i,
  33332                         } };
  33333                     } else {
  33334                         len = arr_like.len;
  33335                         first_idx = i;
  33336                     }
  33337 
  33338                     // Tuples won't participate in the child type resolution. We'll resolve without
  33339                     // them, and if the tuples have a bad type, we'll get a coercion error later.
  33340                     ty_ptr.* = null;
  33341                     val_ptr.* = null;
  33342 
  33343                     continue;
  33344                 }
  33345 
  33346                 if (len) |expect_len| {
  33347                     if (ty.arrayLen(zcu) != expect_len) return .{ .conflict = .{
  33348                         .peer_idx_a = first_idx,
  33349                         .peer_idx_b = i,
  33350                     } };
  33351                 } else {
  33352                     len = ty.arrayLen(zcu);
  33353                     first_idx = i;
  33354                 }
  33355 
  33356                 ty_ptr.* = ty.childType(zcu);
  33357                 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR
  33358             }
  33359 
  33360             const child_ty = switch (try sema.resolvePeerTypesInner(
  33361                 block,
  33362                 src,
  33363                 peer_tys,
  33364                 peer_vals,
  33365             )) {
  33366                 .success => |ty| ty,
  33367                 else => |result| return result,
  33368             };
  33369 
  33370             return .{ .success = try pt.vectorType(.{
  33371                 .len = @intCast(len.?),
  33372                 .child = child_ty.toIntern(),
  33373             }) };
  33374         },
  33375 
  33376         .c_ptr => {
  33377             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  33378             var first_idx: usize = undefined;
  33379             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  33380                 const ty = opt_ty orelse continue;
  33381                 switch (ty.zigTypeTag(zcu)) {
  33382                     .comptime_int => continue, // comptime-known integers can always coerce to C pointers
  33383                     .int => {
  33384                         if (opt_val != null) {
  33385                             // Always allow the coercion for comptime-known ints
  33386                             continue;
  33387                         } else {
  33388                             // Runtime-known, so check if the type is no bigger than a usize
  33389                             const ptr_bits = target.ptrBitWidth();
  33390                             const bits = ty.intInfo(zcu).bits;
  33391                             if (bits <= ptr_bits) continue;
  33392                         }
  33393                     },
  33394                     .null => continue,
  33395                     else => {},
  33396                 }
  33397 
  33398                 if (!ty.isPtrAtRuntime(zcu)) return .{ .conflict = .{
  33399                     .peer_idx_a = strat_reason,
  33400                     .peer_idx_b = i,
  33401                 } };
  33402 
  33403                 // Goes through optionals
  33404                 const peer_info = ty.ptrInfo(zcu);
  33405 
  33406                 var ptr_info = opt_ptr_info orelse {
  33407                     opt_ptr_info = peer_info;
  33408                     opt_ptr_info.?.flags.size = .c;
  33409                     first_idx = i;
  33410                     continue;
  33411                 };
  33412 
  33413                 // Try peer -> cur, then cur -> peer
  33414                 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) orelse {
  33415                     return .{ .conflict = .{
  33416                         .peer_idx_a = first_idx,
  33417                         .peer_idx_b = i,
  33418                     } };
  33419                 }).toIntern();
  33420 
  33421                 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) {
  33422                     const peer_sent = try ip.getCoerced(sema.gpa, pt.tid, ptr_info.sentinel, ptr_info.child);
  33423                     const ptr_sent = try ip.getCoerced(sema.gpa, pt.tid, peer_info.sentinel, ptr_info.child);
  33424                     if (ptr_sent == peer_sent) {
  33425                         ptr_info.sentinel = ptr_sent;
  33426                     } else {
  33427                         ptr_info.sentinel = .none;
  33428                     }
  33429                 } else {
  33430                     ptr_info.sentinel = .none;
  33431                 }
  33432 
  33433                 // Note that the align can be always non-zero; Zcu.ptrType will canonicalize it
  33434                 ptr_info.flags.alignment = InternPool.Alignment.min(
  33435                     if (ptr_info.flags.alignment != .none)
  33436                         ptr_info.flags.alignment
  33437                     else
  33438                         Type.fromInterned(ptr_info.child).abiAlignment(zcu),
  33439 
  33440                     if (peer_info.flags.alignment != .none)
  33441                         peer_info.flags.alignment
  33442                     else
  33443                         Type.fromInterned(peer_info.child).abiAlignment(zcu),
  33444                 );
  33445                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  33446                     return .{ .conflict = .{
  33447                         .peer_idx_a = first_idx,
  33448                         .peer_idx_b = i,
  33449                     } };
  33450                 }
  33451 
  33452                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  33453                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  33454                 {
  33455                     return .{ .conflict = .{
  33456                         .peer_idx_a = first_idx,
  33457                         .peer_idx_b = i,
  33458                     } };
  33459                 }
  33460 
  33461                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  33462                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  33463 
  33464                 opt_ptr_info = ptr_info;
  33465             }
  33466             return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) };
  33467         },
  33468 
  33469         .ptr => {
  33470             // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only
  33471             // if there were no actual slices. Else, we want the slice index to report a conflict.
  33472             var opt_slice_idx: ?usize = null;
  33473 
  33474             var any_abi_aligned = false;
  33475             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  33476             var first_idx: usize = undefined;
  33477             var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error
  33478 
  33479             for (peer_tys, 0..) |opt_ty, i| {
  33480                 const ty = opt_ty orelse continue;
  33481                 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(zcu)) {
  33482                     .pointer => ty.ptrInfo(zcu),
  33483                     .@"fn" => .{
  33484                         .child = ty.toIntern(),
  33485                         .flags = .{
  33486                             .address_space = target_util.defaultAddressSpace(target, .global_constant),
  33487                         },
  33488                     },
  33489                     else => return .{ .conflict = .{
  33490                         .peer_idx_a = strat_reason,
  33491                         .peer_idx_b = i,
  33492                     } },
  33493                 };
  33494 
  33495                 switch (peer_info.flags.size) {
  33496                     .one, .many => {},
  33497                     .slice => opt_slice_idx = i,
  33498                     .c => return .{ .conflict = .{
  33499                         .peer_idx_a = strat_reason,
  33500                         .peer_idx_b = i,
  33501                     } },
  33502                 }
  33503 
  33504                 var ptr_info = opt_ptr_info orelse {
  33505                     opt_ptr_info = peer_info;
  33506                     first_idx = i;
  33507                     continue;
  33508                 };
  33509 
  33510                 other_idx = i;
  33511 
  33512                 // We want to return this in a lot of cases, so alias it here for convenience
  33513                 const generic_err: PeerResolveResult = .{ .conflict = .{
  33514                     .peer_idx_a = first_idx,
  33515                     .peer_idx_b = i,
  33516                 } };
  33517 
  33518                 // Note that the align can be always non-zero; Type.ptr will canonicalize it
  33519                 if (peer_info.flags.alignment == .none) {
  33520                     any_abi_aligned = true;
  33521                 } else if (ptr_info.flags.alignment == .none) {
  33522                     any_abi_aligned = true;
  33523                     ptr_info.flags.alignment = peer_info.flags.alignment;
  33524                 } else {
  33525                     ptr_info.flags.alignment = ptr_info.flags.alignment.minStrict(peer_info.flags.alignment);
  33526                 }
  33527 
  33528                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  33529                     return generic_err;
  33530                 }
  33531 
  33532                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  33533                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  33534                 {
  33535                     return generic_err;
  33536                 }
  33537 
  33538                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  33539                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  33540                 ptr_info.flags.is_allowzero = ptr_info.flags.is_allowzero or peer_info.flags.is_allowzero;
  33541 
  33542                 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) {
  33543                     .one => switch (ip.indexToKey(peer_info.child)) {
  33544                         .array_type => |array_type| array_type.sentinel,
  33545                         else => .none,
  33546                     },
  33547                     .many, .slice => peer_info.sentinel,
  33548                     .c => unreachable,
  33549                 };
  33550 
  33551                 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) {
  33552                     .one => switch (ip.indexToKey(ptr_info.child)) {
  33553                         .array_type => |array_type| array_type.sentinel,
  33554                         else => .none,
  33555                     },
  33556                     .many, .slice => ptr_info.sentinel,
  33557                     .c => unreachable,
  33558                 };
  33559 
  33560                 // We abstract array handling slightly so that tuple pointers can work like array pointers
  33561                 const peer_pointee_array = sema.typeIsArrayLike(.fromInterned(peer_info.child));
  33562                 const cur_pointee_array = sema.typeIsArrayLike(.fromInterned(ptr_info.child));
  33563 
  33564                 // This switch is just responsible for deciding the size and pointee (not including
  33565                 // single-pointer array sentinel).
  33566                 good: {
  33567                     switch (peer_info.flags.size) {
  33568                         .one => switch (ptr_info.flags.size) {
  33569                             .one => {
  33570                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
  33571                                     ptr_info.child = pointee.toIntern();
  33572                                     break :good;
  33573                                 }
  33574 
  33575                                 const cur_arr = cur_pointee_array orelse return generic_err;
  33576                                 const peer_arr = peer_pointee_array orelse return generic_err;
  33577 
  33578                                 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| {
  33579                                     // *[n:x]T + *[n:y]T = *[n]T
  33580                                     if (cur_arr.len == peer_arr.len) {
  33581                                         ptr_info.child = (try pt.arrayType(.{
  33582                                             .len = cur_arr.len,
  33583                                             .child = elem_ty.toIntern(),
  33584                                         })).toIntern();
  33585                                         break :good;
  33586                                     }
  33587                                     // *[a]T + *[b]T = []T
  33588                                     ptr_info.flags.size = .slice;
  33589                                     ptr_info.child = elem_ty.toIntern();
  33590                                     break :good;
  33591                                 }
  33592 
  33593                                 if (peer_arr.elem_ty.toIntern() == .noreturn_type) {
  33594                                     // *struct{} + *[a]T = []T
  33595                                     ptr_info.flags.size = .slice;
  33596                                     ptr_info.child = cur_arr.elem_ty.toIntern();
  33597                                     break :good;
  33598                                 }
  33599 
  33600                                 if (cur_arr.elem_ty.toIntern() == .noreturn_type) {
  33601                                     // *[a]T + *struct{} = []T
  33602                                     ptr_info.flags.size = .slice;
  33603                                     ptr_info.child = peer_arr.elem_ty.toIntern();
  33604                                     break :good;
  33605                                 }
  33606 
  33607                                 return generic_err;
  33608                             },
  33609                             .many => {
  33610                                 // Only works for *[n]T + [*]T -> [*]T
  33611                                 const arr = peer_pointee_array orelse return generic_err;
  33612                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| {
  33613                                     ptr_info.child = pointee.toIntern();
  33614                                     break :good;
  33615                                 }
  33616                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33617                                     // *struct{} + [*]T -> [*]T
  33618                                     break :good;
  33619                                 }
  33620                                 return generic_err;
  33621                             },
  33622                             .slice => {
  33623                                 // Only works for *[n]T + []T -> []T
  33624                                 const arr = peer_pointee_array orelse return generic_err;
  33625                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| {
  33626                                     ptr_info.child = pointee.toIntern();
  33627                                     break :good;
  33628                                 }
  33629                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33630                                     // *struct{} + []T -> []T
  33631                                     break :good;
  33632                                 }
  33633                                 return generic_err;
  33634                             },
  33635                             .c => unreachable,
  33636                         },
  33637                         .many => switch (ptr_info.flags.size) {
  33638                             .one => {
  33639                                 // Only works for [*]T + *[n]T -> [*]T
  33640                                 const arr = cur_pointee_array orelse return generic_err;
  33641                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| {
  33642                                     ptr_info.flags.size = .many;
  33643                                     ptr_info.child = pointee.toIntern();
  33644                                     break :good;
  33645                                 }
  33646                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33647                                     // [*]T + *struct{} -> [*]T
  33648                                     ptr_info.flags.size = .many;
  33649                                     ptr_info.child = peer_info.child;
  33650                                     break :good;
  33651                                 }
  33652                                 return generic_err;
  33653                             },
  33654                             .many => {
  33655                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
  33656                                     ptr_info.child = pointee.toIntern();
  33657                                     break :good;
  33658                                 }
  33659                                 return generic_err;
  33660                             },
  33661                             .slice => {
  33662                                 // Only works if no peers are actually slices
  33663                                 if (opt_slice_idx) |slice_idx| {
  33664                                     return .{ .conflict = .{
  33665                                         .peer_idx_a = slice_idx,
  33666                                         .peer_idx_b = i,
  33667                                     } };
  33668                                 }
  33669                                 // Okay, then works for [*]T + "[]T" -> [*]T
  33670                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
  33671                                     ptr_info.flags.size = .many;
  33672                                     ptr_info.child = pointee.toIntern();
  33673                                     break :good;
  33674                                 }
  33675                                 return generic_err;
  33676                             },
  33677                             .c => unreachable,
  33678                         },
  33679                         .slice => switch (ptr_info.flags.size) {
  33680                             .one => {
  33681                                 // Only works for []T + *[n]T -> []T
  33682                                 const arr = cur_pointee_array orelse return generic_err;
  33683                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| {
  33684                                     ptr_info.flags.size = .slice;
  33685                                     ptr_info.child = pointee.toIntern();
  33686                                     break :good;
  33687                                 }
  33688                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33689                                     // []T + *struct{} -> []T
  33690                                     ptr_info.flags.size = .slice;
  33691                                     ptr_info.child = peer_info.child;
  33692                                     break :good;
  33693                                 }
  33694                                 return generic_err;
  33695                             },
  33696                             .many => {
  33697                                 // Impossible! (current peer is an actual slice)
  33698                                 return generic_err;
  33699                             },
  33700                             .slice => {
  33701                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
  33702                                     ptr_info.child = pointee.toIntern();
  33703                                     break :good;
  33704                                 }
  33705                                 return generic_err;
  33706                             },
  33707                             .c => unreachable,
  33708                         },
  33709                         .c => unreachable,
  33710                     }
  33711                 }
  33712 
  33713                 const sentinel_ty = switch (ptr_info.flags.size) {
  33714                     .one => switch (ip.indexToKey(ptr_info.child)) {
  33715                         .array_type => |array_type| array_type.child,
  33716                         else => ptr_info.child,
  33717                     },
  33718                     .many, .slice, .c => ptr_info.child,
  33719                 };
  33720 
  33721                 sentinel: {
  33722                     no_sentinel: {
  33723                         if (peer_sentinel == .none) break :no_sentinel;
  33724                         if (cur_sentinel == .none) break :no_sentinel;
  33725                         const peer_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, peer_sentinel, sentinel_ty);
  33726                         const cur_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, cur_sentinel, sentinel_ty);
  33727                         if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel;
  33728                         // Sentinels match
  33729                         if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) {
  33730                             .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{
  33731                                 .len = array_type.len,
  33732                                 .child = array_type.child,
  33733                                 .sentinel = cur_sent_coerced,
  33734                             })).toIntern(),
  33735                             else => unreachable,
  33736                         } else {
  33737                             ptr_info.sentinel = cur_sent_coerced;
  33738                         }
  33739                         break :sentinel;
  33740                     }
  33741                     // Clear existing sentinel
  33742                     ptr_info.sentinel = .none;
  33743                     if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) {
  33744                         .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{
  33745                             .len = array_type.len,
  33746                             .child = array_type.child,
  33747                             .sentinel = .none,
  33748                         })).toIntern(),
  33749                         else => {},
  33750                     };
  33751                 }
  33752 
  33753                 opt_ptr_info = ptr_info;
  33754             }
  33755 
  33756             // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance)
  33757             // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to
  33758             // coerce the empty struct to a specific type, but no peer provided one. We need to
  33759             // detect this case and emit an error.
  33760             const pointee = opt_ptr_info.?.child;
  33761             switch (pointee) {
  33762                 .noreturn_type => return .{ .conflict = .{
  33763                     .peer_idx_a = first_idx,
  33764                     .peer_idx_b = other_idx,
  33765                 } },
  33766                 else => switch (ip.indexToKey(pointee)) {
  33767                     .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{
  33768                         .peer_idx_a = first_idx,
  33769                         .peer_idx_b = other_idx,
  33770                     } },
  33771                     else => {},
  33772                 },
  33773             }
  33774 
  33775             if (any_abi_aligned and opt_ptr_info.?.flags.alignment != .none) {
  33776                 opt_ptr_info.?.flags.alignment = opt_ptr_info.?.flags.alignment.minStrict(
  33777                     try Type.fromInterned(pointee).abiAlignmentSema(pt),
  33778                 );
  33779             }
  33780 
  33781             return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) };
  33782         },
  33783 
  33784         .func => {
  33785             var opt_cur_ty: ?Type = null;
  33786             var first_idx: usize = undefined;
  33787             for (peer_tys, 0..) |opt_ty, i| {
  33788                 const ty = opt_ty orelse continue;
  33789                 const cur_ty = opt_cur_ty orelse {
  33790                     opt_cur_ty = ty;
  33791                     first_idx = i;
  33792                     continue;
  33793                 };
  33794                 if (ty.zigTypeTag(zcu) != .@"fn") return .{ .conflict = .{
  33795                     .peer_idx_a = strat_reason,
  33796                     .peer_idx_b = i,
  33797                 } };
  33798                 // ty -> cur_ty
  33799                 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, false, target, src, src)) {
  33800                     continue;
  33801                 }
  33802                 // cur_ty -> ty
  33803                 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, false, target, src, src)) {
  33804                     opt_cur_ty = ty;
  33805                     continue;
  33806                 }
  33807                 return .{ .conflict = .{
  33808                     .peer_idx_a = first_idx,
  33809                     .peer_idx_b = i,
  33810                 } };
  33811             }
  33812             return .{ .success = opt_cur_ty.? };
  33813         },
  33814 
  33815         .enum_or_union => {
  33816             var opt_cur_ty: ?Type = null;
  33817             // The peer index which gave the current type
  33818             var cur_ty_idx: usize = undefined;
  33819 
  33820             for (peer_tys, 0..) |opt_ty, i| {
  33821                 const ty = opt_ty orelse continue;
  33822                 switch (ty.zigTypeTag(zcu)) {
  33823                     .enum_literal, .@"enum", .@"union" => {},
  33824                     else => return .{ .conflict = .{
  33825                         .peer_idx_a = strat_reason,
  33826                         .peer_idx_b = i,
  33827                     } },
  33828                 }
  33829                 const cur_ty = opt_cur_ty orelse {
  33830                     opt_cur_ty = ty;
  33831                     cur_ty_idx = i;
  33832                     continue;
  33833                 };
  33834 
  33835                 // We want to return this in a lot of cases, so alias it here for convenience
  33836                 const generic_err: PeerResolveResult = .{ .conflict = .{
  33837                     .peer_idx_a = cur_ty_idx,
  33838                     .peer_idx_b = i,
  33839                 } };
  33840 
  33841                 switch (cur_ty.zigTypeTag(zcu)) {
  33842                     .enum_literal => {
  33843                         opt_cur_ty = ty;
  33844                         cur_ty_idx = i;
  33845                     },
  33846                     .@"enum" => switch (ty.zigTypeTag(zcu)) {
  33847                         .enum_literal => {},
  33848                         .@"enum" => {
  33849                             if (!ty.eql(cur_ty, zcu)) return generic_err;
  33850                         },
  33851                         .@"union" => {
  33852                             const tag_ty = ty.unionTagTypeHypothetical(zcu);
  33853                             if (!tag_ty.eql(cur_ty, zcu)) return generic_err;
  33854                             opt_cur_ty = ty;
  33855                             cur_ty_idx = i;
  33856                         },
  33857                         else => unreachable,
  33858                     },
  33859                     .@"union" => switch (ty.zigTypeTag(zcu)) {
  33860                         .enum_literal => {},
  33861                         .@"enum" => {
  33862                             const cur_tag_ty = cur_ty.unionTagTypeHypothetical(zcu);
  33863                             if (!ty.eql(cur_tag_ty, zcu)) return generic_err;
  33864                         },
  33865                         .@"union" => {
  33866                             if (!ty.eql(cur_ty, zcu)) return generic_err;
  33867                         },
  33868                         else => unreachable,
  33869                     },
  33870                     else => unreachable,
  33871                 }
  33872             }
  33873             return .{ .success = opt_cur_ty.? };
  33874         },
  33875 
  33876         .comptime_int => {
  33877             for (peer_tys, 0..) |opt_ty, i| {
  33878                 const ty = opt_ty orelse continue;
  33879                 switch (ty.zigTypeTag(zcu)) {
  33880                     .comptime_int => {},
  33881                     else => return .{ .conflict = .{
  33882                         .peer_idx_a = strat_reason,
  33883                         .peer_idx_b = i,
  33884                     } },
  33885                 }
  33886             }
  33887             return .{ .success = .comptime_int };
  33888         },
  33889 
  33890         .comptime_float => {
  33891             for (peer_tys, 0..) |opt_ty, i| {
  33892                 const ty = opt_ty orelse continue;
  33893                 switch (ty.zigTypeTag(zcu)) {
  33894                     .comptime_int, .comptime_float => {},
  33895                     else => return .{ .conflict = .{
  33896                         .peer_idx_a = strat_reason,
  33897                         .peer_idx_b = i,
  33898                     } },
  33899                 }
  33900             }
  33901             return .{ .success = .comptime_float };
  33902         },
  33903 
  33904         .fixed_int => {
  33905             var idx_unsigned: ?usize = null;
  33906             var idx_signed: ?usize = null;
  33907 
  33908             // TODO: this is for compatibility with legacy behavior. See beneath the loop.
  33909             var any_comptime_known = false;
  33910 
  33911             for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| {
  33912                 const ty = opt_ty orelse continue;
  33913                 const opt_val = ptr_opt_val.*;
  33914 
  33915                 const peer_tag = ty.zigTypeTag(zcu);
  33916                 switch (peer_tag) {
  33917                     .comptime_int => {
  33918                         // If the value is undefined, we can't refine to a fixed-width int
  33919                         if (opt_val == null or opt_val.?.isUndef(zcu)) return .{ .conflict = .{
  33920                             .peer_idx_a = strat_reason,
  33921                             .peer_idx_b = i,
  33922                         } };
  33923                         any_comptime_known = true;
  33924                         ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?);
  33925                         continue;
  33926                     },
  33927                     .int => {},
  33928                     else => return .{ .conflict = .{
  33929                         .peer_idx_a = strat_reason,
  33930                         .peer_idx_b = i,
  33931                     } },
  33932                 }
  33933 
  33934                 if (opt_val != null) any_comptime_known = true;
  33935 
  33936                 const info = ty.intInfo(zcu);
  33937 
  33938                 const idx_ptr = switch (info.signedness) {
  33939                     .unsigned => &idx_unsigned,
  33940                     .signed => &idx_signed,
  33941                 };
  33942 
  33943                 const largest_idx = idx_ptr.* orelse {
  33944                     idx_ptr.* = i;
  33945                     continue;
  33946                 };
  33947 
  33948                 const cur_info = peer_tys[largest_idx].?.intInfo(zcu);
  33949                 if (info.bits > cur_info.bits) {
  33950                     idx_ptr.* = i;
  33951                 }
  33952             }
  33953 
  33954             if (idx_signed == null) {
  33955                 return .{ .success = peer_tys[idx_unsigned.?].? };
  33956             }
  33957 
  33958             if (idx_unsigned == null) {
  33959                 return .{ .success = peer_tys[idx_signed.?].? };
  33960             }
  33961 
  33962             const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(zcu);
  33963             const signed_info = peer_tys[idx_signed.?].?.intInfo(zcu);
  33964             if (signed_info.bits > unsigned_info.bits) {
  33965                 return .{ .success = peer_tys[idx_signed.?].? };
  33966             }
  33967 
  33968             // TODO: this is for compatibility with legacy behavior. Before this version of PTR was
  33969             // implemented, the algorithm very often returned false positives, with the expectation
  33970             // that you'd just hit a coercion error later. One of these was that for integers, the
  33971             // largest type would always be returned, even if it couldn't fit everything. This had
  33972             // an unintentional consequence to semantics, which is that if values were known at
  33973             // comptime, they would be coerced down to the smallest type where possible. This
  33974             // behavior is unintuitive and order-dependent, so in my opinion should be eliminated,
  33975             // but for now we'll retain compatibility.
  33976             if (any_comptime_known) {
  33977                 if (unsigned_info.bits > signed_info.bits) {
  33978                     return .{ .success = peer_tys[idx_unsigned.?].? };
  33979                 }
  33980                 const idx = @min(idx_unsigned.?, idx_signed.?);
  33981                 return .{ .success = peer_tys[idx].? };
  33982             }
  33983 
  33984             return .{ .conflict = .{
  33985                 .peer_idx_a = idx_unsigned.?,
  33986                 .peer_idx_b = idx_signed.?,
  33987             } };
  33988         },
  33989 
  33990         .fixed_float => {
  33991             var opt_cur_ty: ?Type = null;
  33992 
  33993             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  33994                 const ty = opt_ty orelse continue;
  33995                 switch (ty.zigTypeTag(zcu)) {
  33996                     .comptime_float, .comptime_int => {},
  33997                     .int => {
  33998                         if (opt_val == null) return .{ .conflict = .{
  33999                             .peer_idx_a = strat_reason,
  34000                             .peer_idx_b = i,
  34001                         } };
  34002                     },
  34003                     .float => {
  34004                         if (opt_cur_ty) |cur_ty| {
  34005                             if (cur_ty.eql(ty, zcu)) continue;
  34006                             // Recreate the type so we eliminate any c_longdouble
  34007                             const bits = @max(cur_ty.floatBits(target), ty.floatBits(target));
  34008                             opt_cur_ty = switch (bits) {
  34009                                 16 => .f16,
  34010                                 32 => .f32,
  34011                                 64 => .f64,
  34012                                 80 => .f80,
  34013                                 128 => .f128,
  34014                                 else => unreachable,
  34015                             };
  34016                         } else {
  34017                             opt_cur_ty = ty;
  34018                         }
  34019                     },
  34020                     else => return .{ .conflict = .{
  34021                         .peer_idx_a = strat_reason,
  34022                         .peer_idx_b = i,
  34023                     } },
  34024                 }
  34025             }
  34026 
  34027             // Note that fixed_float is only chosen if there is at least one fixed-width float peer,
  34028             // so opt_cur_ty must be non-null.
  34029             return .{ .success = opt_cur_ty.? };
  34030         },
  34031 
  34032         .tuple => {
  34033             // First, check that every peer has the same approximate structure (field count)
  34034 
  34035             var opt_first_idx: ?usize = null;
  34036             var is_tuple: bool = undefined;
  34037             var field_count: usize = undefined;
  34038 
  34039             for (peer_tys, 0..) |opt_ty, i| {
  34040                 const ty = opt_ty orelse continue;
  34041 
  34042                 if (!ty.isTuple(zcu)) {
  34043                     return .{ .conflict = .{
  34044                         .peer_idx_a = strat_reason,
  34045                         .peer_idx_b = i,
  34046                     } };
  34047                 }
  34048 
  34049                 const first_idx = opt_first_idx orelse {
  34050                     opt_first_idx = i;
  34051                     is_tuple = ty.isTuple(zcu);
  34052                     field_count = ty.structFieldCount(zcu);
  34053                     continue;
  34054                 };
  34055 
  34056                 if (ty.structFieldCount(zcu) != field_count) {
  34057                     return .{ .conflict = .{
  34058                         .peer_idx_a = first_idx,
  34059                         .peer_idx_b = i,
  34060                     } };
  34061                 }
  34062             }
  34063 
  34064             assert(opt_first_idx != null);
  34065 
  34066             // Now, we'll recursively resolve the field types
  34067             const field_types = try sema.arena.alloc(InternPool.Index, field_count);
  34068             // Values for `comptime` fields - `.none` used for non-comptime fields
  34069             const field_vals = try sema.arena.alloc(InternPool.Index, field_count);
  34070             const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len);
  34071             const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len);
  34072 
  34073             for (field_types, field_vals, 0..) |*field_ty, *field_val, field_index| {
  34074                 // Fill buffers with types and values of the field
  34075                 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| {
  34076                     const ty = opt_ty orelse {
  34077                         peer_field_ty.* = null;
  34078                         peer_field_val.* = null;
  34079                         continue;
  34080                     };
  34081                     peer_field_ty.* = ty.fieldType(field_index, zcu);
  34082                     peer_field_val.* = if (opt_val) |val| try val.fieldValue(pt, field_index) else null;
  34083                 }
  34084 
  34085                 // Resolve field type recursively
  34086                 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) {
  34087                     .success => |ty| ty.toIntern(),
  34088                     else => |result| {
  34089                         const result_buf = try sema.arena.create(PeerResolveResult);
  34090                         result_buf.* = result;
  34091                         const field_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
  34092 
  34093                         // The error info needs the field types, but we can't reuse sub_peer_tys
  34094                         // since the recursive call may have clobbered it.
  34095                         const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len);
  34096                         for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| {
  34097                             // Already-resolved types won't be referenced by the error so it's fine
  34098                             // to leave them undefined.
  34099                             const ty = opt_ty orelse continue;
  34100                             peer_field_ty.* = ty.fieldType(field_index, zcu);
  34101                         }
  34102 
  34103                         return .{ .field_error = .{
  34104                             .field_name = field_name,
  34105                             .field_types = peer_field_tys,
  34106                             .sub_result = result_buf,
  34107                         } };
  34108                     },
  34109                 };
  34110 
  34111                 // Decide if this is a comptime field. If it is comptime in all peers, and the
  34112                 // coerced comptime values are all the same, we say it is comptime, else not.
  34113 
  34114                 var comptime_val: ?Value = null;
  34115                 for (peer_tys) |opt_ty| {
  34116                     const struct_ty = opt_ty orelse continue;
  34117                     try struct_ty.resolveStructFieldInits(pt);
  34118 
  34119                     const uncoerced_field_val = try struct_ty.structFieldValueComptime(pt, field_index) orelse {
  34120                         comptime_val = null;
  34121                         break;
  34122                     };
  34123                     const uncoerced_field = Air.internedToRef(uncoerced_field_val.toIntern());
  34124                     const coerced_inst = sema.coerceExtra(block, .fromInterned(field_ty.*), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) {
  34125                         // 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
  34126                         error.NotCoercible => {
  34127                             comptime_val = null;
  34128                             break;
  34129                         },
  34130                         else => |e| return e,
  34131                     };
  34132                     const coerced_val = (try sema.resolveValue(coerced_inst)) orelse continue;
  34133                     const existing = comptime_val orelse {
  34134                         comptime_val = coerced_val;
  34135                         continue;
  34136                     };
  34137                     if (!coerced_val.eql(existing, .fromInterned(field_ty.*), zcu)) {
  34138                         comptime_val = null;
  34139                         break;
  34140                     }
  34141                 }
  34142 
  34143                 field_val.* = if (comptime_val) |v| v.toIntern() else .none;
  34144             }
  34145 
  34146             const final_ty = try ip.getTupleType(zcu.gpa, pt.tid, .{
  34147                 .types = field_types,
  34148                 .values = field_vals,
  34149             });
  34150 
  34151             return .{ .success = .fromInterned(final_ty) };
  34152         },
  34153 
  34154         .exact => {
  34155             var expect_ty: ?Type = null;
  34156             var first_idx: usize = undefined;
  34157             for (peer_tys, 0..) |opt_ty, i| {
  34158                 const ty = opt_ty orelse continue;
  34159                 if (expect_ty) |expect| {
  34160                     if (!ty.eql(expect, zcu)) return .{ .conflict = .{
  34161                         .peer_idx_a = first_idx,
  34162                         .peer_idx_b = i,
  34163                     } };
  34164                 } else {
  34165                     expect_ty = ty;
  34166                     first_idx = i;
  34167                 }
  34168             }
  34169             return .{ .success = expect_ty.? };
  34170         },
  34171     }
  34172 }
  34173 
  34174 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type {
  34175     // e0 -> e1
  34176     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) {
  34177         return e1;
  34178     }
  34179 
  34180     // e1 -> e0
  34181     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) {
  34182         return e0;
  34183     }
  34184 
  34185     return sema.errorSetMerge(e0, e1);
  34186 }
  34187 
  34188 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type {
  34189     const target = sema.pt.zcu.getTarget();
  34190 
  34191     // ty_b -> ty_a
  34192     if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, false, target, src, src, null)) {
  34193         return ty_a;
  34194     }
  34195 
  34196     // ty_a -> ty_b
  34197     if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, false, target, src, src, null)) {
  34198         return ty_b;
  34199     }
  34200 
  34201     return null;
  34202 }
  34203 
  34204 const ArrayLike = struct {
  34205     len: u64,
  34206     /// `noreturn` indicates that this type is `struct{}` so can coerce to anything
  34207     elem_ty: Type,
  34208 };
  34209 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike {
  34210     const pt = sema.pt;
  34211     const zcu = pt.zcu;
  34212     return switch (ty.zigTypeTag(zcu)) {
  34213         .array => .{
  34214             .len = ty.arrayLen(zcu),
  34215             .elem_ty = ty.childType(zcu),
  34216         },
  34217         .@"struct" => {
  34218             const field_count = ty.structFieldCount(zcu);
  34219             if (field_count == 0) return .{
  34220                 .len = 0,
  34221                 .elem_ty = .noreturn,
  34222             };
  34223             if (!ty.isTuple(zcu)) return null;
  34224             const elem_ty = ty.fieldType(0, zcu);
  34225             for (1..field_count) |i| {
  34226                 if (!ty.fieldType(i, zcu).eql(elem_ty, zcu)) {
  34227                     return null;
  34228                 }
  34229             }
  34230             return .{
  34231                 .len = field_count,
  34232                 .elem_ty = elem_ty,
  34233             };
  34234         },
  34235         else => null,
  34236     };
  34237 }
  34238 
  34239 pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void {
  34240     const pt = sema.pt;
  34241     const zcu = pt.zcu;
  34242     const ip = &zcu.intern_pool;
  34243 
  34244     if (sema.fn_ret_ty_ies) |ies| {
  34245         try sema.resolveInferredErrorSetPtr(block, src, ies);
  34246         assert(ies.resolved != .none);
  34247         ip.funcIesResolved(sema.func_index).* = ies.resolved;
  34248     }
  34249 }
  34250 
  34251 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type, src: LazySrcLoc) CompileError!void {
  34252     const pt = sema.pt;
  34253     const zcu = pt.zcu;
  34254     const ip = &zcu.intern_pool;
  34255     const fn_ty_info = zcu.typeToFunc(fn_ty).?;
  34256 
  34257     try Type.fromInterned(fn_ty_info.return_type).resolveFully(pt);
  34258 
  34259     if (zcu.comp.config.any_error_tracing and
  34260         Type.fromInterned(fn_ty_info.return_type).isError(zcu))
  34261     {
  34262         // Ensure the type exists so that backends can assume that.
  34263         _ = try sema.getBuiltinType(src, .StackTrace);
  34264     }
  34265 
  34266     for (0..fn_ty_info.param_types.len) |i| {
  34267         try Type.fromInterned(fn_ty_info.param_types.get(ip)[i]).resolveFully(pt);
  34268     }
  34269 }
  34270 
  34271 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
  34272     return val.resolveLazy(sema.arena, sema.pt);
  34273 }
  34274 
  34275 /// Resolve a struct's alignment only without triggering resolution of its layout.
  34276 /// Asserts that the alignment is not yet resolved and the layout is non-packed.
  34277 pub fn resolveStructAlignment(
  34278     sema: *Sema,
  34279     ty: InternPool.Index,
  34280     struct_type: InternPool.LoadedStructType,
  34281 ) SemaError!void {
  34282     const pt = sema.pt;
  34283     const zcu = pt.zcu;
  34284     const ip = &zcu.intern_pool;
  34285     const target = zcu.getTarget();
  34286 
  34287     assert(sema.owner.unwrap().type == ty);
  34288 
  34289     assert(struct_type.layout != .@"packed");
  34290     assert(struct_type.flagsUnordered(ip).alignment == .none);
  34291 
  34292     const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
  34293 
  34294     // We'll guess "pointer-aligned", if the struct has an
  34295     // underaligned pointer field then some allocations
  34296     // might require explicit alignment.
  34297     if (struct_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return;
  34298 
  34299     try sema.resolveStructFieldTypes(ty, struct_type);
  34300 
  34301     // We'll guess "pointer-aligned", if the struct has an
  34302     // underaligned pointer field then some allocations
  34303     // might require explicit alignment.
  34304     if (struct_type.assumePointerAlignedIfWip(ip, ptr_align)) return;
  34305     defer struct_type.clearAlignmentWip(ip);
  34306 
  34307     // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
  34308     // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
  34309 
  34310     var alignment: Alignment = .@"1";
  34311 
  34312     for (0..struct_type.field_types.len) |i| {
  34313         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34314         if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt))
  34315             continue;
  34316         const field_align = try field_ty.structFieldAlignmentSema(
  34317             struct_type.fieldAlign(ip, i),
  34318             struct_type.layout,
  34319             pt,
  34320         );
  34321         alignment = alignment.maxStrict(field_align);
  34322     }
  34323 
  34324     struct_type.setAlignment(ip, alignment);
  34325 }
  34326 
  34327 pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
  34328     const pt = sema.pt;
  34329     const zcu = pt.zcu;
  34330     const ip = &zcu.intern_pool;
  34331     const struct_type = zcu.typeToStruct(ty) orelse return;
  34332 
  34333     assert(sema.owner.unwrap().type == ty.toIntern());
  34334 
  34335     if (struct_type.haveLayout(ip))
  34336         return;
  34337 
  34338     try sema.resolveStructFieldTypes(ty.toIntern(), struct_type);
  34339 
  34340     // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
  34341     // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
  34342 
  34343     if (struct_type.layout == .@"packed") {
  34344         sema.backingIntType(struct_type) catch |err| switch (err) {
  34345             error.OutOfMemory, error.AnalysisFail => |e| return e,
  34346             error.ComptimeBreak, error.ComptimeReturn => unreachable,
  34347         };
  34348         return;
  34349     }
  34350 
  34351     if (struct_type.setLayoutWip(ip)) {
  34352         const msg = try sema.errMsg(
  34353             ty.srcLoc(zcu),
  34354             "struct '{f}' depends on itself",
  34355             .{ty.fmt(pt)},
  34356         );
  34357         return sema.failWithOwnedErrorMsg(null, msg);
  34358     }
  34359     defer struct_type.clearLayoutWip(ip);
  34360 
  34361     const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len);
  34362     const sizes = try sema.arena.alloc(u64, struct_type.field_types.len);
  34363 
  34364     var big_align: Alignment = .@"1";
  34365 
  34366     for (aligns, sizes, 0..) |*field_align, *field_size, i| {
  34367         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34368         if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) {
  34369             struct_type.offsets.get(ip)[i] = 0;
  34370             field_size.* = 0;
  34371             field_align.* = .none;
  34372             continue;
  34373         }
  34374 
  34375         field_size.* = field_ty.abiSizeSema(pt) catch |err| switch (err) {
  34376             error.AnalysisFail => {
  34377                 const msg = sema.err orelse return err;
  34378                 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
  34379                 return err;
  34380             },
  34381             else => return err,
  34382         };
  34383         field_align.* = try field_ty.structFieldAlignmentSema(
  34384             struct_type.fieldAlign(ip, i),
  34385             struct_type.layout,
  34386             pt,
  34387         );
  34388         big_align = big_align.maxStrict(field_align.*);
  34389     }
  34390 
  34391     if (struct_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) {
  34392         const msg = try sema.errMsg(
  34393             ty.srcLoc(zcu),
  34394             "struct layout depends on it having runtime bits",
  34395             .{},
  34396         );
  34397         return sema.failWithOwnedErrorMsg(null, msg);
  34398     }
  34399 
  34400     if (struct_type.flagsUnordered(ip).assumed_pointer_aligned and
  34401         big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(zcu.getTarget().ptrBitWidth(), 8))))
  34402     {
  34403         const msg = try sema.errMsg(
  34404             ty.srcLoc(zcu),
  34405             "struct layout depends on being pointer aligned",
  34406             .{},
  34407         );
  34408         return sema.failWithOwnedErrorMsg(null, msg);
  34409     }
  34410 
  34411     if (struct_type.hasReorderedFields()) {
  34412         const runtime_order = struct_type.runtime_order.get(ip);
  34413 
  34414         for (runtime_order, 0..) |*ro, i| {
  34415             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34416             if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) {
  34417                 ro.* = .omitted;
  34418             } else {
  34419                 ro.* = @enumFromInt(i);
  34420             }
  34421         }
  34422 
  34423         const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder;
  34424 
  34425         const AlignSortContext = struct {
  34426             aligns: []const Alignment,
  34427 
  34428             fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool {
  34429                 if (a == .omitted) return false;
  34430                 if (b == .omitted) return true;
  34431                 const a_align = ctx.aligns[@intFromEnum(a)];
  34432                 const b_align = ctx.aligns[@intFromEnum(b)];
  34433                 return a_align.compare(.gt, b_align);
  34434             }
  34435         };
  34436         if (!zcu.backendSupportsFeature(.field_reordering)) {
  34437             // TODO: we should probably also reorder tuple fields? This is a bit weird because it'll involve
  34438             // mutating the `InternPool` for a non-container type.
  34439             //
  34440             // TODO: implement field reordering support in all the backends!
  34441             //
  34442             // This logic does not reorder fields; it only moves the omitted ones to the end
  34443             // so that logic elsewhere does not need to special-case here.
  34444             var i: usize = 0;
  34445             var off: usize = 0;
  34446             while (i + off < runtime_order.len) {
  34447                 if (runtime_order[i + off] == .omitted) {
  34448                     off += 1;
  34449                     continue;
  34450                 }
  34451                 runtime_order[i] = runtime_order[i + off];
  34452                 i += 1;
  34453             }
  34454             @memset(runtime_order[i..], .omitted);
  34455         } else {
  34456             mem.sortUnstable(RuntimeOrder, runtime_order, AlignSortContext{
  34457                 .aligns = aligns,
  34458             }, AlignSortContext.lessThan);
  34459         }
  34460     }
  34461 
  34462     // Calculate size, alignment, and field offsets.
  34463     const offsets = struct_type.offsets.get(ip);
  34464     var it = struct_type.iterateRuntimeOrder(ip);
  34465     var offset: u64 = 0;
  34466     while (it.next()) |i| {
  34467         offsets[i] = @intCast(aligns[i].forward(offset));
  34468         offset = offsets[i] + sizes[i];
  34469     }
  34470     const size = std.math.cast(u32, big_align.forward(offset)) orelse {
  34471         const msg = try sema.errMsg(
  34472             ty.srcLoc(zcu),
  34473             "struct layout requires size {d}, this compiler implementation supports up to {d}",
  34474             .{ big_align.forward(offset), std.math.maxInt(u32) },
  34475         );
  34476         return sema.failWithOwnedErrorMsg(null, msg);
  34477     };
  34478     struct_type.setLayoutResolved(ip, size, big_align);
  34479     _ = try ty.comptimeOnlySema(pt);
  34480 }
  34481 
  34482 fn backingIntType(
  34483     sema: *Sema,
  34484     struct_type: InternPool.LoadedStructType,
  34485 ) CompileError!void {
  34486     const pt = sema.pt;
  34487     const zcu = pt.zcu;
  34488     const gpa = zcu.gpa;
  34489     const ip = &zcu.intern_pool;
  34490 
  34491     var analysis_arena = std.heap.ArenaAllocator.init(gpa);
  34492     defer analysis_arena.deinit();
  34493 
  34494     var block: Block = .{
  34495         .parent = null,
  34496         .sema = sema,
  34497         .namespace = struct_type.namespace,
  34498         .instructions = .{},
  34499         .inlining = null,
  34500         .comptime_reason = null, // set below if needed
  34501         .src_base_inst = struct_type.zir_index,
  34502         .type_name_ctx = struct_type.name,
  34503     };
  34504     defer assert(block.instructions.items.len == 0);
  34505 
  34506     const fields_bit_sum = blk: {
  34507         var accumulator: u64 = 0;
  34508         for (0..struct_type.field_types.len) |i| {
  34509             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34510             accumulator += try field_ty.bitSizeSema(pt);
  34511         }
  34512         break :blk accumulator;
  34513     };
  34514 
  34515     const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir.?;
  34516     const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
  34517     const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
  34518     assert(extended.opcode == .struct_decl);
  34519     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
  34520 
  34521     if (small.has_backing_int) {
  34522         var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
  34523         const captures_len = if (small.has_captures_len) blk: {
  34524             const captures_len = zir.extra[extra_index];
  34525             extra_index += 1;
  34526             break :blk captures_len;
  34527         } else 0;
  34528         extra_index += @intFromBool(small.has_fields_len);
  34529         extra_index += @intFromBool(small.has_decls_len);
  34530 
  34531         extra_index += captures_len * 2;
  34532 
  34533         const backing_int_body_len = zir.extra[extra_index];
  34534         extra_index += 1;
  34535 
  34536         const backing_int_src: LazySrcLoc = .{
  34537             .base_node_inst = struct_type.zir_index,
  34538             .offset = .{ .node_offset_container_tag = .zero },
  34539         };
  34540         block.comptime_reason = .{ .reason = .{
  34541             .src = backing_int_src,
  34542             .r = .{ .simple = .type },
  34543         } };
  34544         const backing_int_ty = blk: {
  34545             if (backing_int_body_len == 0) {
  34546                 const backing_int_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  34547                 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
  34548             } else {
  34549                 const body = zir.bodySlice(extra_index, backing_int_body_len);
  34550                 const ty_ref = try sema.resolveInlineBody(&block, body, zir_index);
  34551                 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
  34552             }
  34553         };
  34554 
  34555         try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum);
  34556         struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
  34557     } else {
  34558         if (fields_bit_sum > std.math.maxInt(u16)) {
  34559             return sema.fail(&block, block.nodeOffset(.zero), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum});
  34560         }
  34561         const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum));
  34562         struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
  34563     }
  34564 
  34565     try sema.flushExports();
  34566 }
  34567 
  34568 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void {
  34569     const pt = sema.pt;
  34570     const zcu = pt.zcu;
  34571 
  34572     if (!backing_int_ty.isInt(zcu)) {
  34573         return sema.fail(block, src, "expected backing integer type, found '{f}'", .{backing_int_ty.fmt(pt)});
  34574     }
  34575     if (backing_int_ty.bitSize(zcu) != fields_bit_sum) {
  34576         return sema.fail(
  34577             block,
  34578             src,
  34579             "backing integer type '{f}' has bit size {d} but the struct fields have a total bit size of {d}",
  34580             .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum },
  34581         );
  34582     }
  34583 }
  34584 
  34585 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  34586     const pt = sema.pt;
  34587     if (!ty.isIndexable(pt.zcu)) {
  34588         const msg = msg: {
  34589             const msg = try sema.errMsg(src, "type '{f}' does not support indexing", .{ty.fmt(pt)});
  34590             errdefer msg.destroy(sema.gpa);
  34591             try sema.errNote(src, msg, "operand must be an array, slice, tuple, or vector", .{});
  34592             break :msg msg;
  34593         };
  34594         return sema.failWithOwnedErrorMsg(block, msg);
  34595     }
  34596 }
  34597 
  34598 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  34599     const pt = sema.pt;
  34600     const zcu = pt.zcu;
  34601     if (ty.zigTypeTag(zcu) == .pointer) {
  34602         switch (ty.ptrSize(zcu)) {
  34603             .slice, .many, .c => return,
  34604             .one => {
  34605                 const elem_ty = ty.childType(zcu);
  34606                 if (elem_ty.zigTypeTag(zcu) == .array) return;
  34607                 // TODO https://github.com/ziglang/zig/issues/15479
  34608                 // if (elem_ty.isTuple()) return;
  34609             },
  34610         }
  34611     }
  34612     const msg = msg: {
  34613         const msg = try sema.errMsg(src, "type '{f}' is not an indexable pointer", .{ty.fmt(pt)});
  34614         errdefer msg.destroy(sema.gpa);
  34615         try sema.errNote(src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{});
  34616         break :msg msg;
  34617     };
  34618     return sema.failWithOwnedErrorMsg(block, msg);
  34619 }
  34620 
  34621 /// Resolve a unions's alignment only without triggering resolution of its layout.
  34622 /// Asserts that the alignment is not yet resolved.
  34623 pub fn resolveUnionAlignment(
  34624     sema: *Sema,
  34625     ty: Type,
  34626     union_type: InternPool.LoadedUnionType,
  34627 ) SemaError!void {
  34628     const pt = sema.pt;
  34629     const zcu = pt.zcu;
  34630     const ip = &zcu.intern_pool;
  34631     const target = zcu.getTarget();
  34632 
  34633     assert(sema.owner.unwrap().type == ty.toIntern());
  34634 
  34635     assert(!union_type.haveLayout(ip));
  34636 
  34637     const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
  34638 
  34639     // We'll guess "pointer-aligned", if the union has an
  34640     // underaligned pointer field then some allocations
  34641     // might require explicit alignment.
  34642     if (union_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return;
  34643 
  34644     try sema.resolveUnionFieldTypes(ty, union_type);
  34645 
  34646     // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
  34647     // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
  34648 
  34649     var max_align: Alignment = .@"1";
  34650     for (0..union_type.field_types.len) |field_index| {
  34651         const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
  34652         if (!(try field_ty.hasRuntimeBitsSema(pt))) continue;
  34653 
  34654         const explicit_align = union_type.fieldAlign(ip, field_index);
  34655         const field_align = if (explicit_align != .none)
  34656             explicit_align
  34657         else
  34658             try field_ty.abiAlignmentSema(sema.pt);
  34659 
  34660         max_align = max_align.max(field_align);
  34661     }
  34662 
  34663     union_type.setAlignment(ip, max_align);
  34664 }
  34665 
  34666 /// This logic must be kept in sync with `Type.getUnionLayout`.
  34667 pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
  34668     const pt = sema.pt;
  34669     const ip = &pt.zcu.intern_pool;
  34670 
  34671     try sema.resolveUnionFieldTypes(ty, ip.loadUnionType(ty.ip_index));
  34672 
  34673     // Load again, since the tag type might have changed due to resolution.
  34674     const union_type = ip.loadUnionType(ty.ip_index);
  34675 
  34676     assert(sema.owner.unwrap().type == ty.toIntern());
  34677 
  34678     const old_flags = union_type.flagsUnordered(ip);
  34679     switch (old_flags.status) {
  34680         .none, .have_field_types => {},
  34681         .field_types_wip, .layout_wip => {
  34682             const msg = try sema.errMsg(
  34683                 ty.srcLoc(pt.zcu),
  34684                 "union '{f}' depends on itself",
  34685                 .{ty.fmt(pt)},
  34686             );
  34687             return sema.failWithOwnedErrorMsg(null, msg);
  34688         },
  34689         .have_layout, .fully_resolved_wip, .fully_resolved => return,
  34690     }
  34691 
  34692     errdefer union_type.setStatusIfLayoutWip(ip, old_flags.status);
  34693 
  34694     union_type.setStatus(ip, .layout_wip);
  34695 
  34696     // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
  34697     // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
  34698 
  34699     var max_size: u64 = 0;
  34700     var max_align: Alignment = .@"1";
  34701     for (0..union_type.field_types.len) |field_index| {
  34702         const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
  34703         if (field_ty.isNoReturn(pt.zcu)) continue;
  34704 
  34705         // We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s,
  34706         // but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type,
  34707         // so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us.
  34708         // This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to
  34709         // be queried, enabling failures to be handled with the emission of a compile error, and also in
  34710         // `hasRuntimeBits` for ever being able to infinite recurse in the first place.
  34711         try field_ty.resolveLayout(pt);
  34712 
  34713         if (try field_ty.hasRuntimeBitsSema(pt)) {
  34714             max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) {
  34715                 error.AnalysisFail => {
  34716                     const msg = sema.err orelse return err;
  34717                     try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{});
  34718                     return err;
  34719                 },
  34720                 else => return err,
  34721             });
  34722         }
  34723 
  34724         const explicit_align = union_type.fieldAlign(ip, field_index);
  34725         const field_align = if (explicit_align != .none)
  34726             explicit_align
  34727         else
  34728             try field_ty.abiAlignmentSema(pt);
  34729         max_align = max_align.max(field_align);
  34730     }
  34731 
  34732     const has_runtime_tag = union_type.flagsUnordered(ip).runtime_tag.hasTag() and
  34733         try Type.fromInterned(union_type.enum_tag_ty).hasRuntimeBitsSema(pt);
  34734     const size, const alignment, const padding = if (has_runtime_tag) layout: {
  34735         const enum_tag_type: Type = .fromInterned(union_type.enum_tag_ty);
  34736         const tag_align = try enum_tag_type.abiAlignmentSema(pt);
  34737         const tag_size = try enum_tag_type.abiSizeSema(pt);
  34738 
  34739         // Put the tag before or after the payload depending on which one's
  34740         // alignment is greater.
  34741         var size: u64 = 0;
  34742         var padding: u32 = 0;
  34743         if (tag_align.order(max_align).compare(.gte)) {
  34744             // {Tag, Payload}
  34745             size += tag_size;
  34746             size = max_align.forward(size);
  34747             size += max_size;
  34748             const prev_size = size;
  34749             size = tag_align.forward(size);
  34750             padding = @intCast(size - prev_size);
  34751         } else {
  34752             // {Payload, Tag}
  34753             size += max_size;
  34754             size = switch (pt.zcu.getTarget().ofmt) {
  34755                 .c => max_align,
  34756                 else => tag_align,
  34757             }.forward(size);
  34758             size += tag_size;
  34759             const prev_size = size;
  34760             size = max_align.forward(size);
  34761             padding = @intCast(size - prev_size);
  34762         }
  34763 
  34764         break :layout .{ size, max_align.max(tag_align), padding };
  34765     } else .{ max_align.forward(max_size), max_align, 0 };
  34766 
  34767     const casted_size = std.math.cast(u32, size) orelse {
  34768         const msg = try sema.errMsg(
  34769             ty.srcLoc(pt.zcu),
  34770             "union layout requires size {d}, this compiler implementation supports up to {d}",
  34771             .{ size, std.math.maxInt(u32) },
  34772         );
  34773         return sema.failWithOwnedErrorMsg(null, msg);
  34774     };
  34775     union_type.setHaveLayout(ip, casted_size, padding, alignment);
  34776 
  34777     if (union_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) {
  34778         const msg = try sema.errMsg(
  34779             ty.srcLoc(pt.zcu),
  34780             "union layout depends on it having runtime bits",
  34781             .{},
  34782         );
  34783         return sema.failWithOwnedErrorMsg(null, msg);
  34784     }
  34785 
  34786     if (union_type.flagsUnordered(ip).assumed_pointer_aligned and
  34787         alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(pt.zcu.getTarget().ptrBitWidth(), 8))))
  34788     {
  34789         const msg = try sema.errMsg(
  34790             ty.srcLoc(pt.zcu),
  34791             "union layout depends on being pointer aligned",
  34792             .{},
  34793         );
  34794         return sema.failWithOwnedErrorMsg(null, msg);
  34795     }
  34796     _ = try ty.comptimeOnlySema(pt);
  34797 }
  34798 
  34799 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to
  34800 /// be resolved.
  34801 pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void {
  34802     try sema.resolveStructLayout(ty);
  34803     try sema.resolveStructFieldInits(ty);
  34804 
  34805     const pt = sema.pt;
  34806     const zcu = pt.zcu;
  34807     const ip = &zcu.intern_pool;
  34808     const struct_type = zcu.typeToStruct(ty).?;
  34809 
  34810     assert(sema.owner.unwrap().type == ty.toIntern());
  34811 
  34812     if (struct_type.setFullyResolved(ip)) return;
  34813     errdefer struct_type.clearFullyResolved(ip);
  34814 
  34815     // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
  34816     // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
  34817 
  34818     // After we have resolve struct layout we have to go over the fields again to
  34819     // make sure pointer fields get their child types resolved as well.
  34820     // See also similar code for unions.
  34821 
  34822     for (0..struct_type.field_types.len) |i| {
  34823         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34824         try field_ty.resolveFully(pt);
  34825     }
  34826 }
  34827 
  34828 pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void {
  34829     try sema.resolveUnionLayout(ty);
  34830 
  34831     const pt = sema.pt;
  34832     const zcu = pt.zcu;
  34833     const ip = &zcu.intern_pool;
  34834     const union_obj = zcu.typeToUnion(ty).?;
  34835 
  34836     assert(sema.owner.unwrap().type == ty.toIntern());
  34837 
  34838     switch (union_obj.flagsUnordered(ip).status) {
  34839         .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
  34840         .fully_resolved_wip, .fully_resolved => return,
  34841     }
  34842 
  34843     // No `zcu.trackUnitSema` calls, since this phase isn't really doing any semantic analysis.
  34844     // It's just triggering *other* analysis, alongside a simple loop over already-resolved info.
  34845 
  34846     {
  34847         // After we have resolve union layout we have to go over the fields again to
  34848         // make sure pointer fields get their child types resolved as well.
  34849         // See also similar code for structs.
  34850         const prev_status = union_obj.flagsUnordered(ip).status;
  34851         errdefer union_obj.setStatus(ip, prev_status);
  34852 
  34853         union_obj.setStatus(ip, .fully_resolved_wip);
  34854         for (0..union_obj.field_types.len) |field_index| {
  34855             const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  34856             try field_ty.resolveFully(pt);
  34857         }
  34858         union_obj.setStatus(ip, .fully_resolved);
  34859     }
  34860 
  34861     // And let's not forget comptime-only status.
  34862     _ = try ty.comptimeOnlySema(pt);
  34863 }
  34864 
  34865 pub fn resolveStructFieldTypes(
  34866     sema: *Sema,
  34867     ty: InternPool.Index,
  34868     struct_type: InternPool.LoadedStructType,
  34869 ) SemaError!void {
  34870     const pt = sema.pt;
  34871     const zcu = pt.zcu;
  34872     const ip = &zcu.intern_pool;
  34873 
  34874     assert(sema.owner.unwrap().type == ty);
  34875 
  34876     if (struct_type.haveFieldTypes(ip)) return;
  34877 
  34878     if (struct_type.setFieldTypesWip(ip)) {
  34879         const msg = try sema.errMsg(
  34880             Type.fromInterned(ty).srcLoc(zcu),
  34881             "struct '{f}' depends on itself",
  34882             .{Type.fromInterned(ty).fmt(pt)},
  34883         );
  34884         return sema.failWithOwnedErrorMsg(null, msg);
  34885     }
  34886     defer struct_type.clearFieldTypesWip(ip);
  34887 
  34888     // can't happen earlier than this because we only want the progress node if not already resolved
  34889     const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null);
  34890     defer tracked_unit.end(zcu);
  34891 
  34892     sema.structFields(struct_type) catch |err| switch (err) {
  34893         error.AnalysisFail, error.OutOfMemory => |e| return e,
  34894         error.ComptimeBreak, error.ComptimeReturn => unreachable,
  34895     };
  34896 }
  34897 
  34898 pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
  34899     const pt = sema.pt;
  34900     const zcu = pt.zcu;
  34901     const ip = &zcu.intern_pool;
  34902     const struct_type = zcu.typeToStruct(ty) orelse return;
  34903 
  34904     assert(sema.owner.unwrap().type == ty.toIntern());
  34905 
  34906     // Inits can start as resolved
  34907     if (struct_type.haveFieldInits(ip)) return;
  34908 
  34909     try sema.resolveStructLayout(ty);
  34910 
  34911     if (struct_type.setInitsWip(ip)) {
  34912         const msg = try sema.errMsg(
  34913             ty.srcLoc(zcu),
  34914             "struct '{f}' depends on itself",
  34915             .{ty.fmt(pt)},
  34916         );
  34917         return sema.failWithOwnedErrorMsg(null, msg);
  34918     }
  34919     defer struct_type.clearInitsWip(ip);
  34920 
  34921     // can't happen earlier than this because we only want the progress node if not already resolved
  34922     const tracked_unit = zcu.trackUnitSema(struct_type.name.toSlice(ip), null);
  34923     defer tracked_unit.end(zcu);
  34924 
  34925     sema.structFieldInits(struct_type) catch |err| switch (err) {
  34926         error.AnalysisFail, error.OutOfMemory => |e| return e,
  34927         error.ComptimeBreak, error.ComptimeReturn => unreachable,
  34928     };
  34929     struct_type.setHaveFieldInits(ip);
  34930 }
  34931 
  34932 pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.LoadedUnionType) SemaError!void {
  34933     const pt = sema.pt;
  34934     const zcu = pt.zcu;
  34935     const ip = &zcu.intern_pool;
  34936 
  34937     assert(sema.owner.unwrap().type == ty.toIntern());
  34938 
  34939     switch (union_type.flagsUnordered(ip).status) {
  34940         .none => {},
  34941         .field_types_wip => {
  34942             const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)});
  34943             return sema.failWithOwnedErrorMsg(null, msg);
  34944         },
  34945         .have_field_types,
  34946         .have_layout,
  34947         .layout_wip,
  34948         .fully_resolved_wip,
  34949         .fully_resolved,
  34950         => return,
  34951     }
  34952 
  34953     // can't happen earlier than this because we only want the progress node if not already resolved
  34954     const tracked_unit = zcu.trackUnitSema(union_type.name.toSlice(ip), null);
  34955     defer tracked_unit.end(zcu);
  34956 
  34957     union_type.setStatus(ip, .field_types_wip);
  34958     errdefer union_type.setStatus(ip, .none);
  34959     sema.unionFields(ty.toIntern(), union_type) catch |err| switch (err) {
  34960         error.AnalysisFail, error.OutOfMemory => |e| return e,
  34961         error.ComptimeBreak, error.ComptimeReturn => unreachable,
  34962     };
  34963     union_type.setStatus(ip, .have_field_types);
  34964 }
  34965 
  34966 /// Returns a normal error set corresponding to the fully populated inferred
  34967 /// error set.
  34968 fn resolveInferredErrorSet(
  34969     sema: *Sema,
  34970     block: *Block,
  34971     src: LazySrcLoc,
  34972     ies_index: InternPool.Index,
  34973 ) CompileError!InternPool.Index {
  34974     const pt = sema.pt;
  34975     const zcu = pt.zcu;
  34976     const ip = &zcu.intern_pool;
  34977     const func_index = ip.iesFuncIndex(ies_index);
  34978     const func = zcu.funcInfo(func_index);
  34979 
  34980     try sema.declareDependency(.{ .interned = func_index }); // resolved IES
  34981 
  34982     try zcu.maybeUnresolveIes(func_index);
  34983     const resolved_ty = func.resolvedErrorSetUnordered(ip);
  34984     if (resolved_ty != .none) return resolved_ty;
  34985 
  34986     if (zcu.analysis_in_progress.contains(AnalUnit.wrap(.{ .func = func_index }))) {
  34987         return sema.fail(block, src, "unable to resolve inferred error set", .{});
  34988     }
  34989 
  34990     // In order to ensure that all dependencies are properly added to the set,
  34991     // we need to ensure the function body is analyzed of the inferred error
  34992     // set. However, in the case of comptime/inline function calls with
  34993     // inferred error sets, each call gets an adhoc InferredErrorSet object, which
  34994     // has no corresponding function body.
  34995     const ies_func_info = zcu.typeToFunc(.fromInterned(func.ty)).?;
  34996     // if ies declared by a inline function with generic return type, the return_type should be generic_poison,
  34997     // because inline function does not create a new declaration, and the ies has been filled with analyzeCall,
  34998     // so here we can simply skip this case.
  34999     if (ies_func_info.return_type == .generic_poison_type) {
  35000         assert(ies_func_info.cc == .@"inline");
  35001     } else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) {
  35002         if (ies_func_info.is_generic) {
  35003             return sema.failWithOwnedErrorMsg(block, msg: {
  35004                 const msg = try sema.errMsg(src, "unable to resolve inferred error set of generic function", .{});
  35005                 errdefer msg.destroy(sema.gpa);
  35006                 try sema.errNote(zcu.navSrcLoc(func.owner_nav), msg, "generic function declared here", .{});
  35007                 break :msg msg;
  35008             });
  35009         }
  35010         // In this case we are dealing with the actual InferredErrorSet object that
  35011         // corresponds to the function, not one created to track an inline/comptime call.
  35012         const orig_func_index = ip.unwrapCoercedFunc(func_index);
  35013         try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_func_index }));
  35014         try pt.ensureFuncBodyUpToDate(orig_func_index);
  35015     }
  35016 
  35017     // This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody`
  35018     // which calls `resolveInferredErrorSetPtr`.
  35019     const final_resolved_ty = func.resolvedErrorSetUnordered(ip);
  35020     assert(final_resolved_ty != .none);
  35021     return final_resolved_ty;
  35022 }
  35023 
  35024 pub fn resolveInferredErrorSetPtr(
  35025     sema: *Sema,
  35026     block: *Block,
  35027     src: LazySrcLoc,
  35028     ies: *InferredErrorSet,
  35029 ) CompileError!void {
  35030     const pt = sema.pt;
  35031     const ip = &pt.zcu.intern_pool;
  35032 
  35033     if (ies.resolved != .none) return;
  35034 
  35035     const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern());
  35036 
  35037     for (ies.inferred_error_sets.keys()) |other_ies_index| {
  35038         if (ies_index == other_ies_index) continue;
  35039         switch (try sema.resolveInferredErrorSet(block, src, other_ies_index)) {
  35040             .anyerror_type => {
  35041                 ies.resolved = .anyerror_type;
  35042                 return;
  35043             },
  35044             else => |error_set_ty_index| {
  35045                 const names = ip.indexToKey(error_set_ty_index).error_set_type.names;
  35046                 for (names.get(ip)) |name| {
  35047                     try ies.errors.put(sema.arena, name, {});
  35048                 }
  35049             },
  35050         }
  35051     }
  35052 
  35053     const resolved_error_set_ty = try pt.errorSetFromUnsortedNames(ies.errors.keys());
  35054     ies.resolved = resolved_error_set_ty.toIntern();
  35055 }
  35056 
  35057 fn resolveAdHocInferredErrorSet(
  35058     sema: *Sema,
  35059     block: *Block,
  35060     src: LazySrcLoc,
  35061     value: InternPool.Index,
  35062 ) CompileError!InternPool.Index {
  35063     const pt = sema.pt;
  35064     const zcu = pt.zcu;
  35065     const gpa = sema.gpa;
  35066     const ip = &zcu.intern_pool;
  35067     const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value));
  35068     if (new_ty == .none) return value;
  35069     return ip.getCoerced(gpa, pt.tid, value, new_ty);
  35070 }
  35071 
  35072 fn resolveAdHocInferredErrorSetTy(
  35073     sema: *Sema,
  35074     block: *Block,
  35075     src: LazySrcLoc,
  35076     ty: InternPool.Index,
  35077 ) CompileError!InternPool.Index {
  35078     const ies = sema.fn_ret_ty_ies orelse return .none;
  35079     const pt = sema.pt;
  35080     const zcu = pt.zcu;
  35081     const ip = &zcu.intern_pool;
  35082     const error_union_info = switch (ip.indexToKey(ty)) {
  35083         .error_union_type => |x| x,
  35084         else => return .none,
  35085     };
  35086     if (error_union_info.error_set_type != .adhoc_inferred_error_set_type)
  35087         return .none;
  35088 
  35089     try sema.resolveInferredErrorSetPtr(block, src, ies);
  35090     const new_ty = try pt.intern(.{ .error_union_type = .{
  35091         .error_set_type = ies.resolved,
  35092         .payload_type = error_union_info.payload_type,
  35093     } });
  35094     return new_ty;
  35095 }
  35096 
  35097 fn resolveInferredErrorSetTy(
  35098     sema: *Sema,
  35099     block: *Block,
  35100     src: LazySrcLoc,
  35101     ty: InternPool.Index,
  35102 ) CompileError!InternPool.Index {
  35103     const pt = sema.pt;
  35104     const zcu = pt.zcu;
  35105     const ip = &zcu.intern_pool;
  35106     if (ty == .anyerror_type) return ty;
  35107     switch (ip.indexToKey(ty)) {
  35108         .error_set_type => return ty,
  35109         .inferred_error_set_type => return sema.resolveInferredErrorSet(block, src, ty),
  35110         else => unreachable,
  35111     }
  35112 }
  35113 
  35114 fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct {
  35115     /// fields_len
  35116     usize,
  35117     Zir.Inst.StructDecl.Small,
  35118     /// extra_index
  35119     usize,
  35120 } {
  35121     const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
  35122     assert(extended.opcode == .struct_decl);
  35123     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
  35124     var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
  35125 
  35126     const captures_len = if (small.has_captures_len) blk: {
  35127         const captures_len = zir.extra[extra_index];
  35128         extra_index += 1;
  35129         break :blk captures_len;
  35130     } else 0;
  35131 
  35132     const fields_len = if (small.has_fields_len) blk: {
  35133         const fields_len = zir.extra[extra_index];
  35134         extra_index += 1;
  35135         break :blk fields_len;
  35136     } else 0;
  35137 
  35138     const decls_len = if (small.has_decls_len) decls_len: {
  35139         const decls_len = zir.extra[extra_index];
  35140         extra_index += 1;
  35141         break :decls_len decls_len;
  35142     } else 0;
  35143 
  35144     extra_index += captures_len * 2;
  35145 
  35146     // The backing integer cannot be handled until `resolveStructLayout()`.
  35147     if (small.has_backing_int) {
  35148         const backing_int_body_len = zir.extra[extra_index];
  35149         extra_index += 1; // backing_int_body_len
  35150         if (backing_int_body_len == 0) {
  35151             extra_index += 1; // backing_int_ref
  35152         } else {
  35153             extra_index += backing_int_body_len; // backing_int_body_inst
  35154         }
  35155     }
  35156 
  35157     // Skip over decls.
  35158     extra_index += decls_len;
  35159 
  35160     return .{ fields_len, small, extra_index };
  35161 }
  35162 
  35163 fn structFields(
  35164     sema: *Sema,
  35165     struct_type: InternPool.LoadedStructType,
  35166 ) CompileError!void {
  35167     const pt = sema.pt;
  35168     const zcu = pt.zcu;
  35169     const gpa = zcu.gpa;
  35170     const ip = &zcu.intern_pool;
  35171     const namespace_index = struct_type.namespace;
  35172     const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
  35173     const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
  35174 
  35175     const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
  35176 
  35177     if (fields_len == 0) switch (struct_type.layout) {
  35178         .@"packed" => {
  35179             try sema.backingIntType(struct_type);
  35180             return;
  35181         },
  35182         .auto, .@"extern" => {
  35183             struct_type.setLayoutResolved(ip, 0, .none);
  35184             return;
  35185         },
  35186     };
  35187 
  35188     var block_scope: Block = .{
  35189         .parent = null,
  35190         .sema = sema,
  35191         .namespace = namespace_index,
  35192         .instructions = .{},
  35193         .inlining = null,
  35194         .comptime_reason = .{ .reason = .{
  35195             .src = .{
  35196                 .base_node_inst = struct_type.zir_index,
  35197                 .offset = .nodeOffset(.zero),
  35198             },
  35199             .r = .{ .simple = .struct_fields },
  35200         } },
  35201         .src_base_inst = struct_type.zir_index,
  35202         .type_name_ctx = struct_type.name,
  35203     };
  35204     defer assert(block_scope.instructions.items.len == 0);
  35205 
  35206     const Field = struct {
  35207         type_body_len: u32 = 0,
  35208         align_body_len: u32 = 0,
  35209         init_body_len: u32 = 0,
  35210         type_ref: Zir.Inst.Ref = .none,
  35211     };
  35212     const fields = try sema.arena.alloc(Field, fields_len);
  35213 
  35214     var any_inits = false;
  35215     var any_aligned = false;
  35216 
  35217     {
  35218         const bits_per_field = 4;
  35219         const fields_per_u32 = 32 / bits_per_field;
  35220         const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  35221         const flags_index = extra_index;
  35222         var bit_bag_index: usize = flags_index;
  35223         extra_index += bit_bags_count;
  35224         var cur_bit_bag: u32 = undefined;
  35225         var field_i: u32 = 0;
  35226         while (field_i < fields_len) : (field_i += 1) {
  35227             if (field_i % fields_per_u32 == 0) {
  35228                 cur_bit_bag = zir.extra[bit_bag_index];
  35229                 bit_bag_index += 1;
  35230             }
  35231             const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  35232             cur_bit_bag >>= 1;
  35233             const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
  35234             cur_bit_bag >>= 1;
  35235             const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0;
  35236             cur_bit_bag >>= 1;
  35237             const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
  35238             cur_bit_bag >>= 1;
  35239 
  35240             if (is_comptime) struct_type.setFieldComptime(ip, field_i);
  35241 
  35242             const field_name_zir: [:0]const u8 = zir.nullTerminatedString(@enumFromInt(zir.extra[extra_index]));
  35243             extra_index += 1; // field_name
  35244 
  35245             fields[field_i] = .{};
  35246 
  35247             if (has_type_body) {
  35248                 fields[field_i].type_body_len = zir.extra[extra_index];
  35249             } else {
  35250                 fields[field_i].type_ref = @enumFromInt(zir.extra[extra_index]);
  35251             }
  35252             extra_index += 1;
  35253 
  35254             // This string needs to outlive the ZIR code.
  35255             const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
  35256             assert(struct_type.addFieldName(ip, field_name) == null);
  35257 
  35258             if (has_align) {
  35259                 fields[field_i].align_body_len = zir.extra[extra_index];
  35260                 extra_index += 1;
  35261                 any_aligned = true;
  35262             }
  35263             if (has_init) {
  35264                 fields[field_i].init_body_len = zir.extra[extra_index];
  35265                 extra_index += 1;
  35266                 any_inits = true;
  35267             }
  35268         }
  35269     }
  35270 
  35271     // Next we do only types and alignments, saving the inits for a second pass,
  35272     // so that init values may depend on type layout.
  35273 
  35274     for (fields, 0..) |zir_field, field_i| {
  35275         const ty_src: LazySrcLoc = .{
  35276             .base_node_inst = struct_type.zir_index,
  35277             .offset = .{ .container_field_type = @intCast(field_i) },
  35278         };
  35279         const field_ty: Type = ty: {
  35280             if (zir_field.type_ref != .none) {
  35281                 break :ty try sema.resolveType(&block_scope, ty_src, zir_field.type_ref);
  35282             }
  35283             assert(zir_field.type_body_len != 0);
  35284             const body = zir.bodySlice(extra_index, zir_field.type_body_len);
  35285             extra_index += body.len;
  35286             const ty_ref = try sema.resolveInlineBody(&block_scope, body, zir_index);
  35287             break :ty try sema.analyzeAsType(&block_scope, ty_src, ty_ref);
  35288         };
  35289 
  35290         struct_type.field_types.get(ip)[field_i] = field_ty.toIntern();
  35291 
  35292         if (field_ty.zigTypeTag(zcu) == .@"opaque") {
  35293             const msg = msg: {
  35294                 const msg = try sema.errMsg(ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  35295                 errdefer msg.destroy(sema.gpa);
  35296 
  35297                 try sema.addDeclaredHereNote(msg, field_ty);
  35298                 break :msg msg;
  35299             };
  35300             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35301         }
  35302         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  35303             const msg = msg: {
  35304                 const msg = try sema.errMsg(ty_src, "struct fields cannot be 'noreturn'", .{});
  35305                 errdefer msg.destroy(sema.gpa);
  35306 
  35307                 try sema.addDeclaredHereNote(msg, field_ty);
  35308                 break :msg msg;
  35309             };
  35310             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35311         }
  35312         switch (struct_type.layout) {
  35313             .@"extern" => if (!try sema.validateExternType(field_ty, .struct_field)) {
  35314                 const msg = msg: {
  35315                     const msg = try sema.errMsg(ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  35316                     errdefer msg.destroy(sema.gpa);
  35317 
  35318                     try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .struct_field);
  35319 
  35320                     try sema.addDeclaredHereNote(msg, field_ty);
  35321                     break :msg msg;
  35322                 };
  35323                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35324             },
  35325             .@"packed" => if (!try sema.validatePackedType(field_ty)) {
  35326                 const msg = msg: {
  35327                     const msg = try sema.errMsg(ty_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  35328                     errdefer msg.destroy(sema.gpa);
  35329 
  35330                     try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty);
  35331 
  35332                     try sema.addDeclaredHereNote(msg, field_ty);
  35333                     break :msg msg;
  35334                 };
  35335                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35336             },
  35337             else => {},
  35338         }
  35339 
  35340         if (zir_field.align_body_len > 0) {
  35341             const body = zir.bodySlice(extra_index, zir_field.align_body_len);
  35342             extra_index += body.len;
  35343             const align_ref = try sema.resolveInlineBody(&block_scope, body, zir_index);
  35344             const align_src: LazySrcLoc = .{
  35345                 .base_node_inst = struct_type.zir_index,
  35346                 .offset = .{ .container_field_align = @intCast(field_i) },
  35347             };
  35348             const field_align = try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
  35349             struct_type.field_aligns.get(ip)[field_i] = field_align;
  35350         }
  35351 
  35352         extra_index += zir_field.init_body_len;
  35353     }
  35354 
  35355     struct_type.clearFieldTypesWip(ip);
  35356     if (!any_inits) struct_type.setHaveFieldInits(ip);
  35357 
  35358     try sema.flushExports();
  35359 }
  35360 
  35361 // This logic must be kept in sync with `structFields`
  35362 fn structFieldInits(
  35363     sema: *Sema,
  35364     struct_type: InternPool.LoadedStructType,
  35365 ) CompileError!void {
  35366     const pt = sema.pt;
  35367     const zcu = pt.zcu;
  35368     const ip = &zcu.intern_pool;
  35369 
  35370     assert(!struct_type.haveFieldInits(ip));
  35371 
  35372     const namespace_index = struct_type.namespace;
  35373     const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
  35374     const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
  35375     const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
  35376 
  35377     var block_scope: Block = .{
  35378         .parent = null,
  35379         .sema = sema,
  35380         .namespace = namespace_index,
  35381         .instructions = .{},
  35382         .inlining = null,
  35383         .comptime_reason = undefined, // set when `block_scope` is used
  35384         .src_base_inst = struct_type.zir_index,
  35385         .type_name_ctx = struct_type.name,
  35386     };
  35387     defer assert(block_scope.instructions.items.len == 0);
  35388 
  35389     const Field = struct {
  35390         type_body_len: u32 = 0,
  35391         align_body_len: u32 = 0,
  35392         init_body_len: u32 = 0,
  35393     };
  35394     const fields = try sema.arena.alloc(Field, fields_len);
  35395 
  35396     var any_inits = false;
  35397 
  35398     {
  35399         const bits_per_field = 4;
  35400         const fields_per_u32 = 32 / bits_per_field;
  35401         const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  35402         const flags_index = extra_index;
  35403         var bit_bag_index: usize = flags_index;
  35404         extra_index += bit_bags_count;
  35405         var cur_bit_bag: u32 = undefined;
  35406         var field_i: u32 = 0;
  35407         while (field_i < fields_len) : (field_i += 1) {
  35408             if (field_i % fields_per_u32 == 0) {
  35409                 cur_bit_bag = zir.extra[bit_bag_index];
  35410                 bit_bag_index += 1;
  35411             }
  35412             const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  35413             cur_bit_bag >>= 1;
  35414             const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
  35415             cur_bit_bag >>= 2;
  35416             const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
  35417             cur_bit_bag >>= 1;
  35418 
  35419             extra_index += 1; // field_name
  35420 
  35421             fields[field_i] = .{};
  35422 
  35423             if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index];
  35424             extra_index += 1;
  35425 
  35426             if (has_align) {
  35427                 fields[field_i].align_body_len = zir.extra[extra_index];
  35428                 extra_index += 1;
  35429             }
  35430             if (has_init) {
  35431                 fields[field_i].init_body_len = zir.extra[extra_index];
  35432                 extra_index += 1;
  35433                 any_inits = true;
  35434             }
  35435         }
  35436     }
  35437 
  35438     if (any_inits) {
  35439         for (fields, 0..) |zir_field, field_i| {
  35440             extra_index += zir_field.type_body_len;
  35441             extra_index += zir_field.align_body_len;
  35442             const body = zir.bodySlice(extra_index, zir_field.init_body_len);
  35443             extra_index += zir_field.init_body_len;
  35444 
  35445             if (body.len == 0) continue;
  35446 
  35447             // Pre-populate the type mapping the body expects to be there.
  35448             // In init bodies, the zir index of the struct itself is used
  35449             // to refer to the current field type.
  35450 
  35451             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_i]);
  35452             const type_ref = Air.internedToRef(field_ty.toIntern());
  35453             try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index});
  35454             sema.inst_map.putAssumeCapacity(zir_index, type_ref);
  35455 
  35456             const init_src: LazySrcLoc = .{
  35457                 .base_node_inst = struct_type.zir_index,
  35458                 .offset = .{ .container_field_value = @intCast(field_i) },
  35459             };
  35460 
  35461             block_scope.comptime_reason = .{ .reason = .{
  35462                 .src = init_src,
  35463                 .r = .{ .simple = .struct_field_default_value },
  35464             } };
  35465             const init = try sema.resolveInlineBody(&block_scope, body, zir_index);
  35466             const coerced = try sema.coerce(&block_scope, field_ty, init, init_src);
  35467             const default_val = try sema.resolveConstValue(&block_scope, init_src, coerced, null);
  35468 
  35469             if (default_val.canMutateComptimeVarState(zcu)) {
  35470                 const field_name = struct_type.fieldName(ip, field_i).unwrap().?;
  35471                 return sema.failWithContainsReferenceToComptimeVar(&block_scope, init_src, field_name, "field default value", default_val);
  35472             }
  35473             struct_type.field_inits.get(ip)[field_i] = default_val.toIntern();
  35474         }
  35475     }
  35476 
  35477     try sema.flushExports();
  35478 }
  35479 
  35480 fn unionFields(
  35481     sema: *Sema,
  35482     union_ty: InternPool.Index,
  35483     union_type: InternPool.LoadedUnionType,
  35484 ) CompileError!void {
  35485     const tracy = trace(@src());
  35486     defer tracy.end();
  35487 
  35488     const pt = sema.pt;
  35489     const zcu = pt.zcu;
  35490     const gpa = zcu.gpa;
  35491     const ip = &zcu.intern_pool;
  35492     const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?;
  35493     const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
  35494     const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
  35495     assert(extended.opcode == .union_decl);
  35496     const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
  35497     const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
  35498     var extra_index: usize = extra.end;
  35499 
  35500     const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: {
  35501         const ty_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  35502         extra_index += 1;
  35503         break :blk ty_ref;
  35504     } else .none;
  35505 
  35506     const captures_len = if (small.has_captures_len) blk: {
  35507         const captures_len = zir.extra[extra_index];
  35508         extra_index += 1;
  35509         break :blk captures_len;
  35510     } else 0;
  35511 
  35512     const body_len = if (small.has_body_len) blk: {
  35513         const body_len = zir.extra[extra_index];
  35514         extra_index += 1;
  35515         break :blk body_len;
  35516     } else 0;
  35517 
  35518     const fields_len = if (small.has_fields_len) blk: {
  35519         const fields_len = zir.extra[extra_index];
  35520         extra_index += 1;
  35521         break :blk fields_len;
  35522     } else 0;
  35523 
  35524     const decls_len = if (small.has_decls_len) decls_len: {
  35525         const decls_len = zir.extra[extra_index];
  35526         extra_index += 1;
  35527         break :decls_len decls_len;
  35528     } else 0;
  35529 
  35530     // Skip over captures and decls.
  35531     extra_index += captures_len * 2 + decls_len;
  35532 
  35533     const body = zir.bodySlice(extra_index, body_len);
  35534     extra_index += body.len;
  35535 
  35536     const src: LazySrcLoc = .{
  35537         .base_node_inst = union_type.zir_index,
  35538         .offset = .nodeOffset(.zero),
  35539     };
  35540 
  35541     var block_scope: Block = .{
  35542         .parent = null,
  35543         .sema = sema,
  35544         .namespace = union_type.namespace,
  35545         .instructions = .{},
  35546         .inlining = null,
  35547         .comptime_reason = .{ .reason = .{
  35548             .src = src,
  35549             .r = .{ .simple = .union_fields },
  35550         } },
  35551         .src_base_inst = union_type.zir_index,
  35552         .type_name_ctx = union_type.name,
  35553     };
  35554     defer assert(block_scope.instructions.items.len == 0);
  35555 
  35556     if (body.len != 0) {
  35557         _ = try sema.analyzeInlineBody(&block_scope, body, zir_index);
  35558     }
  35559 
  35560     var int_tag_ty: Type = undefined;
  35561     var enum_field_names: []InternPool.NullTerminatedString = &.{};
  35562     var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty;
  35563     var explicit_tags_seen: []bool = &.{};
  35564     if (tag_type_ref != .none) {
  35565         const tag_ty_src: LazySrcLoc = .{
  35566             .base_node_inst = union_type.zir_index,
  35567             .offset = .{ .node_offset_container_tag = .zero },
  35568         };
  35569         const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref);
  35570         if (small.auto_enum_tag) {
  35571             // The provided type is an integer type and we must construct the enum tag type here.
  35572             int_tag_ty = provided_ty;
  35573             if (int_tag_ty.zigTypeTag(zcu) != .int and int_tag_ty.zigTypeTag(zcu) != .comptime_int) {
  35574                 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{f}'", .{int_tag_ty.fmt(pt)});
  35575             }
  35576 
  35577             if (fields_len > 0) {
  35578                 const field_count_val = try pt.intValue(.comptime_int, fields_len - 1);
  35579                 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {
  35580                     const msg = msg: {
  35581                         const msg = try sema.errMsg(tag_ty_src, "specified integer tag type cannot represent every field", .{});
  35582                         errdefer msg.destroy(sema.gpa);
  35583                         try sema.errNote(tag_ty_src, msg, "type '{f}' cannot fit values in range 0...{d}", .{
  35584                             int_tag_ty.fmt(pt),
  35585                             fields_len - 1,
  35586                         });
  35587                         break :msg msg;
  35588                     };
  35589                     return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35590                 }
  35591                 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  35592                 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len);
  35593             }
  35594         } else {
  35595             // The provided type is the enum tag type.
  35596             const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) {
  35597                 .enum_type => ip.loadEnumType(provided_ty.toIntern()),
  35598                 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{f}'", .{provided_ty.fmt(pt)}),
  35599             };
  35600             union_type.setTagType(ip, provided_ty.toIntern());
  35601             // The fields of the union must match the enum exactly.
  35602             // A flag per field is used to check for missing and extraneous fields.
  35603             explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
  35604             @memset(explicit_tags_seen, false);
  35605         }
  35606     } else {
  35607         // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
  35608         // purposes, we still auto-generate an enum tag type the same way. That the union is
  35609         // untagged is represented by the Type tag (union vs union_tagged).
  35610         enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  35611     }
  35612 
  35613     var field_types: std.ArrayListUnmanaged(InternPool.Index) = .empty;
  35614     var field_aligns: std.ArrayListUnmanaged(InternPool.Alignment) = .empty;
  35615 
  35616     try field_types.ensureTotalCapacityPrecise(sema.arena, fields_len);
  35617     if (small.any_aligned_fields)
  35618         try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len);
  35619 
  35620     var max_bits: u64 = 0;
  35621     var min_bits: u64 = std.math.maxInt(u64);
  35622     var max_bits_src: LazySrcLoc = undefined;
  35623     var min_bits_src: LazySrcLoc = undefined;
  35624     var max_bits_ty: Type = undefined;
  35625     var min_bits_ty: Type = undefined;
  35626     const bits_per_field = 4;
  35627     const fields_per_u32 = 32 / bits_per_field;
  35628     const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  35629     var bit_bag_index: usize = extra_index;
  35630     extra_index += bit_bags_count;
  35631     var cur_bit_bag: u32 = undefined;
  35632     var field_i: u32 = 0;
  35633     var last_tag_val: ?Value = null;
  35634     const layout = union_type.flagsUnordered(ip).layout;
  35635     while (field_i < fields_len) : (field_i += 1) {
  35636         if (field_i % fields_per_u32 == 0) {
  35637             cur_bit_bag = zir.extra[bit_bag_index];
  35638             bit_bag_index += 1;
  35639         }
  35640         const has_type = @as(u1, @truncate(cur_bit_bag)) != 0;
  35641         cur_bit_bag >>= 1;
  35642         const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  35643         cur_bit_bag >>= 1;
  35644         const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0;
  35645         cur_bit_bag >>= 1;
  35646         const unused = @as(u1, @truncate(cur_bit_bag)) != 0;
  35647         cur_bit_bag >>= 1;
  35648         _ = unused;
  35649 
  35650         const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]);
  35651         const field_name_zir = zir.nullTerminatedString(field_name_index);
  35652         extra_index += 1;
  35653 
  35654         const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
  35655             const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  35656             extra_index += 1;
  35657             break :blk field_type_ref;
  35658         } else .none;
  35659 
  35660         const align_ref: Zir.Inst.Ref = if (has_align) blk: {
  35661             const align_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  35662             extra_index += 1;
  35663             break :blk align_ref;
  35664         } else .none;
  35665 
  35666         const tag_ref: Air.Inst.Ref = if (has_tag) blk: {
  35667             const tag_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  35668             extra_index += 1;
  35669             break :blk try sema.resolveInst(tag_ref);
  35670         } else .none;
  35671 
  35672         const name_src: LazySrcLoc = .{
  35673             .base_node_inst = union_type.zir_index,
  35674             .offset = .{ .container_field_name = field_i },
  35675         };
  35676         const value_src: LazySrcLoc = .{
  35677             .base_node_inst = union_type.zir_index,
  35678             .offset = .{ .container_field_value = field_i },
  35679         };
  35680         const align_src: LazySrcLoc = .{
  35681             .base_node_inst = union_type.zir_index,
  35682             .offset = .{ .container_field_align = field_i },
  35683         };
  35684         const type_src: LazySrcLoc = .{
  35685             .base_node_inst = union_type.zir_index,
  35686             .offset = .{ .container_field_type = field_i },
  35687         };
  35688 
  35689         if (enum_field_vals.capacity() > 0) {
  35690             const enum_tag_val = if (tag_ref != .none) blk: {
  35691                 const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, value_src);
  35692                 const val = try sema.resolveConstDefinedValue(&block_scope, value_src, coerced, .{ .simple = .enum_field_tag_value });
  35693                 last_tag_val = val;
  35694 
  35695                 break :blk val;
  35696             } else blk: {
  35697                 if (last_tag_val) |last_tag| {
  35698                     const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag);
  35699                     if (result.overflow) return sema.fail(
  35700                         &block_scope,
  35701                         value_src,
  35702                         "enumeration value '{f}' too large for type '{f}'",
  35703                         .{ result.val.fmtValueSema(pt, sema), int_tag_ty.fmt(pt) },
  35704                     );
  35705                     last_tag_val = result.val;
  35706                 } else {
  35707                     last_tag_val = try pt.intValue(int_tag_ty, 0);
  35708                 }
  35709                 break :blk last_tag_val.?;
  35710             };
  35711             const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern());
  35712             if (gop.found_existing) {
  35713                 const other_value_src: LazySrcLoc = .{
  35714                     .base_node_inst = union_type.zir_index,
  35715                     .offset = .{ .container_field_value = @intCast(gop.index) },
  35716                 };
  35717                 const msg = msg: {
  35718                     const msg = try sema.errMsg(
  35719                         value_src,
  35720                         "enum tag value {f} already taken",
  35721                         .{enum_tag_val.fmtValueSema(pt, sema)},
  35722                     );
  35723                     errdefer msg.destroy(gpa);
  35724                     try sema.errNote(other_value_src, msg, "other occurrence here", .{});
  35725                     break :msg msg;
  35726                 };
  35727                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35728             }
  35729         }
  35730 
  35731         // This string needs to outlive the ZIR code.
  35732         const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
  35733         if (enum_field_names.len != 0) {
  35734             enum_field_names[field_i] = field_name;
  35735         }
  35736 
  35737         const field_ty: Type = if (!has_type)
  35738             .void
  35739         else if (field_type_ref == .none)
  35740             .noreturn
  35741         else
  35742             try sema.resolveType(&block_scope, type_src, field_type_ref);
  35743 
  35744         if (explicit_tags_seen.len > 0) {
  35745             const tag_ty = union_type.tagTypeUnordered(ip);
  35746             const tag_info = ip.loadEnumType(tag_ty);
  35747             const enum_index = tag_info.nameIndex(ip, field_name) orelse {
  35748                 return sema.fail(&block_scope, name_src, "no field named '{f}' in enum '{f}'", .{
  35749                     field_name.fmt(ip), Type.fromInterned(tag_ty).fmt(pt),
  35750                 });
  35751             };
  35752 
  35753             // No check for duplicate because the check already happened in order
  35754             // to create the enum type in the first place.
  35755             assert(!explicit_tags_seen[enum_index]);
  35756             explicit_tags_seen[enum_index] = true;
  35757 
  35758             // Enforce the enum fields and the union fields being in the same order.
  35759             if (enum_index != field_i) {
  35760                 const msg = msg: {
  35761                     const enum_field_src: LazySrcLoc = .{
  35762                         .base_node_inst = Type.fromInterned(tag_ty).typeDeclInstAllowGeneratedTag(zcu).?,
  35763                         .offset = .{ .container_field_name = enum_index },
  35764                     };
  35765                     const msg = try sema.errMsg(name_src, "union field '{f}' ordered differently than corresponding enum field", .{
  35766                         field_name.fmt(ip),
  35767                     });
  35768                     errdefer msg.destroy(sema.gpa);
  35769                     try sema.errNote(enum_field_src, msg, "enum field here", .{});
  35770                     break :msg msg;
  35771                 };
  35772                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35773             }
  35774         }
  35775 
  35776         if (field_ty.zigTypeTag(zcu) == .@"opaque") {
  35777             const msg = msg: {
  35778                 const msg = try sema.errMsg(type_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  35779                 errdefer msg.destroy(sema.gpa);
  35780 
  35781                 try sema.addDeclaredHereNote(msg, field_ty);
  35782                 break :msg msg;
  35783             };
  35784             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35785         }
  35786         switch (layout) {
  35787             .@"extern" => if (!try sema.validateExternType(field_ty, .union_field)) {
  35788                 const msg = msg: {
  35789                     const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  35790                     errdefer msg.destroy(sema.gpa);
  35791 
  35792                     try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field);
  35793 
  35794                     try sema.addDeclaredHereNote(msg, field_ty);
  35795                     break :msg msg;
  35796                 };
  35797                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35798             },
  35799             .@"packed" => {
  35800                 if (!try sema.validatePackedType(field_ty)) {
  35801                     const msg = msg: {
  35802                         const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  35803                         errdefer msg.destroy(sema.gpa);
  35804 
  35805                         try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty);
  35806 
  35807                         try sema.addDeclaredHereNote(msg, field_ty);
  35808                         break :msg msg;
  35809                     };
  35810                     return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35811                 }
  35812                 const field_bits = try field_ty.bitSizeSema(pt);
  35813                 if (field_bits >= max_bits) {
  35814                     max_bits = field_bits;
  35815                     max_bits_src = type_src;
  35816                     max_bits_ty = field_ty;
  35817                 }
  35818                 if (field_bits <= min_bits) {
  35819                     min_bits = field_bits;
  35820                     min_bits_src = type_src;
  35821                     min_bits_ty = field_ty;
  35822                 }
  35823             },
  35824             .auto => {},
  35825         }
  35826 
  35827         field_types.appendAssumeCapacity(field_ty.toIntern());
  35828 
  35829         if (small.any_aligned_fields) {
  35830             field_aligns.appendAssumeCapacity(if (align_ref != .none)
  35831                 try sema.resolveAlign(&block_scope, align_src, align_ref)
  35832             else
  35833                 .none);
  35834         } else {
  35835             assert(align_ref == .none);
  35836         }
  35837     }
  35838 
  35839     union_type.setFieldTypes(ip, field_types.items);
  35840     union_type.setFieldAligns(ip, field_aligns.items);
  35841 
  35842     if (layout == .@"packed" and fields_len != 0 and min_bits != max_bits) {
  35843         const msg = msg: {
  35844             const msg = try sema.errMsg(src, "packed union has fields with mismatching bit sizes", .{});
  35845             errdefer msg.destroy(sema.gpa);
  35846             try sema.errNote(min_bits_src, msg, "{d} bits here", .{min_bits});
  35847             try sema.addDeclaredHereNote(msg, min_bits_ty);
  35848             try sema.errNote(max_bits_src, msg, "{d} bits here", .{max_bits});
  35849             try sema.addDeclaredHereNote(msg, max_bits_ty);
  35850             break :msg msg;
  35851         };
  35852         return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35853     }
  35854 
  35855     if (explicit_tags_seen.len > 0) {
  35856         const tag_ty = union_type.tagTypeUnordered(ip);
  35857         const tag_info = ip.loadEnumType(tag_ty);
  35858         if (tag_info.names.len > fields_len) {
  35859             const msg = msg: {
  35860                 const msg = try sema.errMsg(src, "enum field(s) missing in union", .{});
  35861                 errdefer msg.destroy(sema.gpa);
  35862 
  35863                 for (tag_info.names.get(ip), 0..) |field_name, field_index| {
  35864                     if (explicit_tags_seen[field_index]) continue;
  35865                     try sema.addFieldErrNote(.fromInterned(tag_ty), field_index, msg, "field '{f}' missing, declared here", .{
  35866                         field_name.fmt(ip),
  35867                     });
  35868                 }
  35869                 try sema.addDeclaredHereNote(msg, .fromInterned(tag_ty));
  35870                 break :msg msg;
  35871             };
  35872             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35873         }
  35874     } else if (enum_field_vals.count() > 0) {
  35875         const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_ty, union_type.name);
  35876         union_type.setTagType(ip, enum_ty);
  35877     } else {
  35878         const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_ty, union_type.name);
  35879         union_type.setTagType(ip, enum_ty);
  35880     }
  35881 
  35882     try sema.flushExports();
  35883 }
  35884 
  35885 fn generateUnionTagTypeNumbered(
  35886     sema: *Sema,
  35887     block: *Block,
  35888     enum_field_names: []const InternPool.NullTerminatedString,
  35889     enum_field_vals: []const InternPool.Index,
  35890     union_type: InternPool.Index,
  35891     union_name: InternPool.NullTerminatedString,
  35892 ) !InternPool.Index {
  35893     const pt = sema.pt;
  35894     const zcu = pt.zcu;
  35895     const gpa = sema.gpa;
  35896     const ip = &zcu.intern_pool;
  35897 
  35898     const name = try ip.getOrPutStringFmt(
  35899         gpa,
  35900         pt.tid,
  35901         "@typeInfo({f}).@\"union\".tag_type.?",
  35902         .{union_name.fmt(ip)},
  35903         .no_embedded_nulls,
  35904     );
  35905 
  35906     const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{
  35907         .name = name,
  35908         .owner_union_ty = union_type,
  35909         .tag_ty = if (enum_field_vals.len == 0)
  35910             (try pt.intType(.unsigned, 0)).toIntern()
  35911         else
  35912             ip.typeOf(enum_field_vals[0]),
  35913         .names = enum_field_names,
  35914         .values = enum_field_vals,
  35915         .tag_mode = .explicit,
  35916         .parent_namespace = block.namespace,
  35917     });
  35918 
  35919     return enum_ty;
  35920 }
  35921 
  35922 fn generateUnionTagTypeSimple(
  35923     sema: *Sema,
  35924     block: *Block,
  35925     enum_field_names: []const InternPool.NullTerminatedString,
  35926     union_type: InternPool.Index,
  35927     union_name: InternPool.NullTerminatedString,
  35928 ) !InternPool.Index {
  35929     const pt = sema.pt;
  35930     const zcu = pt.zcu;
  35931     const ip = &zcu.intern_pool;
  35932     const gpa = sema.gpa;
  35933 
  35934     const name = try ip.getOrPutStringFmt(
  35935         gpa,
  35936         pt.tid,
  35937         "@typeInfo({f}).@\"union\".tag_type.?",
  35938         .{union_name.fmt(ip)},
  35939         .no_embedded_nulls,
  35940     );
  35941 
  35942     const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{
  35943         .name = name,
  35944         .owner_union_ty = union_type,
  35945         .tag_ty = (try pt.smallestUnsignedInt(enum_field_names.len -| 1)).toIntern(),
  35946         .names = enum_field_names,
  35947         .values = &.{},
  35948         .tag_mode = .auto,
  35949         .parent_namespace = block.namespace,
  35950     });
  35951 
  35952     return enum_ty;
  35953 }
  35954 
  35955 /// There is another implementation of this in `Type.onePossibleValue`. This one
  35956 /// in `Sema` is for calling during semantic analysis, and performs field resolution
  35957 /// to get the answer. The one in `Type` is for calling during codegen and asserts
  35958 /// that the types are already resolved.
  35959 /// TODO assert the return value matches `ty.onePossibleValue`
  35960 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
  35961     const pt = sema.pt;
  35962     const zcu = pt.zcu;
  35963     const ip = &zcu.intern_pool;
  35964     return switch (ty.toIntern()) {
  35965         .u0_type,
  35966         .i0_type,
  35967         => try pt.intValue(ty, 0),
  35968         .u1_type,
  35969         .u8_type,
  35970         .i8_type,
  35971         .u16_type,
  35972         .i16_type,
  35973         .u29_type,
  35974         .u32_type,
  35975         .i32_type,
  35976         .u64_type,
  35977         .i64_type,
  35978         .u80_type,
  35979         .u128_type,
  35980         .i128_type,
  35981         .u256_type,
  35982         .usize_type,
  35983         .isize_type,
  35984         .c_char_type,
  35985         .c_short_type,
  35986         .c_ushort_type,
  35987         .c_int_type,
  35988         .c_uint_type,
  35989         .c_long_type,
  35990         .c_ulong_type,
  35991         .c_longlong_type,
  35992         .c_ulonglong_type,
  35993         .c_longdouble_type,
  35994         .f16_type,
  35995         .f32_type,
  35996         .f64_type,
  35997         .f80_type,
  35998         .f128_type,
  35999         .anyopaque_type,
  36000         .bool_type,
  36001         .type_type,
  36002         .anyerror_type,
  36003         .adhoc_inferred_error_set_type,
  36004         .comptime_int_type,
  36005         .comptime_float_type,
  36006         .enum_literal_type,
  36007         .ptr_usize_type,
  36008         .ptr_const_comptime_int_type,
  36009         .manyptr_u8_type,
  36010         .manyptr_const_u8_type,
  36011         .manyptr_const_u8_sentinel_0_type,
  36012         .slice_const_u8_type,
  36013         .slice_const_u8_sentinel_0_type,
  36014         .vector_8_i8_type,
  36015         .vector_16_i8_type,
  36016         .vector_32_i8_type,
  36017         .vector_64_i8_type,
  36018         .vector_1_u8_type,
  36019         .vector_2_u8_type,
  36020         .vector_4_u8_type,
  36021         .vector_8_u8_type,
  36022         .vector_16_u8_type,
  36023         .vector_32_u8_type,
  36024         .vector_64_u8_type,
  36025         .vector_2_i16_type,
  36026         .vector_4_i16_type,
  36027         .vector_8_i16_type,
  36028         .vector_16_i16_type,
  36029         .vector_32_i16_type,
  36030         .vector_4_u16_type,
  36031         .vector_8_u16_type,
  36032         .vector_16_u16_type,
  36033         .vector_32_u16_type,
  36034         .vector_2_i32_type,
  36035         .vector_4_i32_type,
  36036         .vector_8_i32_type,
  36037         .vector_16_i32_type,
  36038         .vector_4_u32_type,
  36039         .vector_8_u32_type,
  36040         .vector_16_u32_type,
  36041         .vector_2_i64_type,
  36042         .vector_4_i64_type,
  36043         .vector_8_i64_type,
  36044         .vector_2_u64_type,
  36045         .vector_4_u64_type,
  36046         .vector_8_u64_type,
  36047         .vector_1_u128_type,
  36048         .vector_2_u128_type,
  36049         .vector_1_u256_type,
  36050         .vector_4_f16_type,
  36051         .vector_8_f16_type,
  36052         .vector_16_f16_type,
  36053         .vector_32_f16_type,
  36054         .vector_2_f32_type,
  36055         .vector_4_f32_type,
  36056         .vector_8_f32_type,
  36057         .vector_16_f32_type,
  36058         .vector_2_f64_type,
  36059         .vector_4_f64_type,
  36060         .vector_8_f64_type,
  36061         .anyerror_void_error_union_type,
  36062         => null,
  36063         .void_type => Value.void,
  36064         .noreturn_type => Value.@"unreachable",
  36065         .anyframe_type => unreachable,
  36066         .null_type => Value.null,
  36067         .undefined_type => Value.undef,
  36068         .optional_noreturn_type => try pt.nullValue(ty),
  36069         .generic_poison_type => unreachable,
  36070         .empty_tuple_type => Value.empty_tuple,
  36071         // values, not types
  36072         .undef,
  36073         .undef_bool,
  36074         .undef_usize,
  36075         .undef_u1,
  36076         .zero,
  36077         .zero_usize,
  36078         .zero_u1,
  36079         .zero_u8,
  36080         .one,
  36081         .one_usize,
  36082         .one_u1,
  36083         .one_u8,
  36084         .four_u8,
  36085         .negative_one,
  36086         .void_value,
  36087         .unreachable_value,
  36088         .null_value,
  36089         .bool_true,
  36090         .bool_false,
  36091         .empty_tuple,
  36092         // invalid
  36093         .none,
  36094         => unreachable,
  36095 
  36096         _ => switch (ty.toIntern().unwrap(ip).getTag(ip)) {
  36097             .removed => unreachable,
  36098 
  36099             .type_int_signed, // i0 handled above
  36100             .type_int_unsigned, // u0 handled above
  36101             .type_pointer,
  36102             .type_slice,
  36103             .type_anyframe,
  36104             .type_error_union,
  36105             .type_anyerror_union,
  36106             .type_error_set,
  36107             .type_inferred_error_set,
  36108             .type_opaque,
  36109             .type_function,
  36110             => null,
  36111 
  36112             .simple_type, // handled above
  36113             // values, not types
  36114             .undef,
  36115             .simple_value,
  36116             .ptr_nav,
  36117             .ptr_uav,
  36118             .ptr_uav_aligned,
  36119             .ptr_comptime_alloc,
  36120             .ptr_comptime_field,
  36121             .ptr_int,
  36122             .ptr_eu_payload,
  36123             .ptr_opt_payload,
  36124             .ptr_elem,
  36125             .ptr_field,
  36126             .ptr_slice,
  36127             .opt_payload,
  36128             .opt_null,
  36129             .int_u8,
  36130             .int_u16,
  36131             .int_u32,
  36132             .int_i32,
  36133             .int_usize,
  36134             .int_comptime_int_u32,
  36135             .int_comptime_int_i32,
  36136             .int_small,
  36137             .int_positive,
  36138             .int_negative,
  36139             .int_lazy_align,
  36140             .int_lazy_size,
  36141             .error_set_error,
  36142             .error_union_error,
  36143             .error_union_payload,
  36144             .enum_literal,
  36145             .enum_tag,
  36146             .float_f16,
  36147             .float_f32,
  36148             .float_f64,
  36149             .float_f80,
  36150             .float_f128,
  36151             .float_c_longdouble_f80,
  36152             .float_c_longdouble_f128,
  36153             .float_comptime_float,
  36154             .variable,
  36155             .threadlocal_variable,
  36156             .@"extern",
  36157             .func_decl,
  36158             .func_instance,
  36159             .func_coerced,
  36160             .only_possible_value,
  36161             .union_value,
  36162             .bytes,
  36163             .aggregate,
  36164             .repeated,
  36165             // memoized value, not types
  36166             .memoized_call,
  36167             => unreachable,
  36168 
  36169             .type_array_big,
  36170             .type_array_small,
  36171             .type_vector,
  36172             .type_enum_auto,
  36173             .type_enum_explicit,
  36174             .type_enum_nonexhaustive,
  36175             .type_struct,
  36176             .type_struct_packed,
  36177             .type_struct_packed_inits,
  36178             .type_tuple,
  36179             .type_union,
  36180             => switch (ip.indexToKey(ty.toIntern())) {
  36181                 inline .array_type, .vector_type => |seq_type, seq_tag| {
  36182                     const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none;
  36183                     if (seq_type.len + @intFromBool(has_sentinel) == 0) return try pt.aggregateValue(ty, &.{});
  36184                     if (try sema.typeHasOnePossibleValue(.fromInterned(seq_type.child))) |opv| {
  36185                         return try pt.aggregateSplatValue(ty, opv);
  36186                     }
  36187                     return null;
  36188                 },
  36189 
  36190                 .struct_type => {
  36191                     // Resolving the layout first helps to avoid loops.
  36192                     // If the type has a coherent layout, we can recurse through fields safely.
  36193                     try ty.resolveLayout(pt);
  36194 
  36195                     const struct_type = ip.loadStructType(ty.toIntern());
  36196 
  36197                     if (struct_type.field_types.len == 0) {
  36198                         // In this case the struct has no fields at all and
  36199                         // therefore has one possible value.
  36200                         return try pt.aggregateValue(ty, &.{});
  36201                     }
  36202 
  36203                     const field_vals = try sema.arena.alloc(
  36204                         InternPool.Index,
  36205                         struct_type.field_types.len,
  36206                     );
  36207                     for (field_vals, 0..) |*field_val, i| {
  36208                         if (struct_type.fieldIsComptime(ip, i)) {
  36209                             try ty.resolveStructFieldInits(pt);
  36210                             field_val.* = struct_type.field_inits.get(ip)[i];
  36211                             continue;
  36212                         }
  36213                         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  36214                         if (try sema.typeHasOnePossibleValue(field_ty)) |field_opv| {
  36215                             field_val.* = field_opv.toIntern();
  36216                         } else return null;
  36217                     }
  36218 
  36219                     // In this case the struct has no runtime-known fields and
  36220                     // therefore has one possible value.
  36221                     return try pt.aggregateValue(ty, field_vals);
  36222                 },
  36223 
  36224                 .tuple_type => |tuple| {
  36225                     for (tuple.values.get(ip)) |val| {
  36226                         if (val == .none) return null;
  36227                     }
  36228                     // In this case the struct has all comptime-known fields and
  36229                     // therefore has one possible value.
  36230                     // TODO: write something like getCoercedInts to avoid needing to dupe
  36231                     return try pt.aggregateValue(ty, try sema.arena.dupe(InternPool.Index, tuple.values.get(ip)));
  36232                 },
  36233 
  36234                 .union_type => {
  36235                     // Resolving the layout first helps to avoid loops.
  36236                     // If the type has a coherent layout, we can recurse through fields safely.
  36237                     try ty.resolveLayout(pt);
  36238 
  36239                     const union_obj = ip.loadUnionType(ty.toIntern());
  36240                     const tag_val = (try sema.typeHasOnePossibleValue(.fromInterned(union_obj.tagTypeUnordered(ip)))) orelse
  36241                         return null;
  36242                     if (union_obj.field_types.len == 0) {
  36243                         const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() });
  36244                         return Value.fromInterned(only);
  36245                     }
  36246                     const only_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[0]);
  36247                     const val_val = (try sema.typeHasOnePossibleValue(only_field_ty)) orelse
  36248                         return null;
  36249                     const only = try pt.internUnion(.{
  36250                         .ty = ty.toIntern(),
  36251                         .tag = tag_val.toIntern(),
  36252                         .val = val_val.toIntern(),
  36253                     });
  36254                     return Value.fromInterned(only);
  36255                 },
  36256 
  36257                 .enum_type => {
  36258                     const enum_type = ip.loadEnumType(ty.toIntern());
  36259                     switch (enum_type.tag_mode) {
  36260                         .nonexhaustive => {
  36261                             if (enum_type.tag_ty == .comptime_int_type) return null;
  36262 
  36263                             if (try sema.typeHasOnePossibleValue(.fromInterned(enum_type.tag_ty))) |int_opv| {
  36264                                 const only = try pt.intern(.{ .enum_tag = .{
  36265                                     .ty = ty.toIntern(),
  36266                                     .int = int_opv.toIntern(),
  36267                                 } });
  36268                                 return Value.fromInterned(only);
  36269                             }
  36270 
  36271                             return null;
  36272                         },
  36273                         .auto, .explicit => {
  36274                             if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(zcu)) return null;
  36275 
  36276                             return Value.fromInterned(switch (enum_type.names.len) {
  36277                                 0 => try pt.intern(.{ .empty_enum_value = ty.toIntern() }),
  36278                                 1 => try pt.intern(.{ .enum_tag = .{
  36279                                     .ty = ty.toIntern(),
  36280                                     .int = if (enum_type.values.len == 0)
  36281                                         (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern()
  36282                                     else
  36283                                         try ip.getCoercedInts(
  36284                                             zcu.gpa,
  36285                                             pt.tid,
  36286                                             ip.indexToKey(enum_type.values.get(ip)[0]).int,
  36287                                             enum_type.tag_ty,
  36288                                         ),
  36289                                 } }),
  36290                                 else => return null,
  36291                             });
  36292                         },
  36293                     }
  36294                 },
  36295 
  36296                 else => unreachable,
  36297             },
  36298 
  36299             .type_optional => {
  36300                 const payload_ip = ip.indexToKey(ty.toIntern()).opt_type;
  36301                 // Although ?noreturn is handled above, the element type
  36302                 // can be effectively noreturn for example via an empty
  36303                 // enum or error set.
  36304                 if (ip.isNoReturn(payload_ip)) return try pt.nullValue(ty);
  36305                 return null;
  36306             },
  36307         },
  36308     };
  36309 }
  36310 
  36311 /// Returns the type of the AIR instruction.
  36312 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
  36313     return sema.getTmpAir().typeOf(inst, &sema.pt.zcu.intern_pool);
  36314 }
  36315 
  36316 pub fn getTmpAir(sema: Sema) Air {
  36317     return .{
  36318         .instructions = sema.air_instructions.slice(),
  36319         .extra = sema.air_extra,
  36320     };
  36321 }
  36322 
  36323 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
  36324     const fields = std.meta.fields(@TypeOf(extra));
  36325     try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len);
  36326     return sema.addExtraAssumeCapacity(extra);
  36327 }
  36328 
  36329 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
  36330     const result: u32 = @intCast(sema.air_extra.items.len);
  36331     sema.air_extra.appendSliceAssumeCapacity(&payloadToExtraItems(extra));
  36332     return result;
  36333 }
  36334 
  36335 fn payloadToExtraItems(data: anytype) [@typeInfo(@TypeOf(data)).@"struct".fields.len]u32 {
  36336     const fields = @typeInfo(@TypeOf(data)).@"struct".fields;
  36337     var result: [fields.len]u32 = undefined;
  36338     inline for (&result, fields) |*val, field| {
  36339         val.* = switch (field.type) {
  36340             u32 => @field(data, field.name),
  36341             i32, Air.CondBr.BranchHints, Air.Asm.Flags => @bitCast(@field(data, field.name)),
  36342             Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(data, field.name)),
  36343             else => @compileError("bad field type: " ++ @typeName(field.type)),
  36344         };
  36345     }
  36346     return result;
  36347 }
  36348 
  36349 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void {
  36350     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(refs));
  36351 }
  36352 
  36353 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index {
  36354     const air_datas = sema.air_instructions.items(.data);
  36355     const air_tags = sema.air_instructions.items(.tag);
  36356     switch (air_tags[@intFromEnum(inst_index)]) {
  36357         .br => return air_datas[@intFromEnum(inst_index)].br.block_inst,
  36358         else => return null,
  36359     }
  36360 }
  36361 
  36362 fn isComptimeKnown(
  36363     sema: *Sema,
  36364     inst: Air.Inst.Ref,
  36365 ) !bool {
  36366     return (try sema.resolveValue(inst)) != null;
  36367 }
  36368 
  36369 fn analyzeComptimeAlloc(
  36370     sema: *Sema,
  36371     block: *Block,
  36372     src: LazySrcLoc,
  36373     var_type: Type,
  36374     alignment: Alignment,
  36375 ) CompileError!Air.Inst.Ref {
  36376     const pt = sema.pt;
  36377     const zcu = pt.zcu;
  36378 
  36379     // Needed to make an anon decl with type `var_type` (the `finish()` call below).
  36380     _ = try sema.typeHasOnePossibleValue(var_type);
  36381 
  36382     const ptr_type = try pt.ptrTypeSema(.{
  36383         .child = var_type.toIntern(),
  36384         .flags = .{
  36385             .alignment = alignment,
  36386             .address_space = target_util.defaultAddressSpace(zcu.getTarget(), .global_constant),
  36387         },
  36388     });
  36389 
  36390     const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment);
  36391 
  36392     return Air.internedToRef((try pt.intern(.{ .ptr = .{
  36393         .ty = ptr_type.toIntern(),
  36394         .base_addr = .{ .comptime_alloc = alloc },
  36395         .byte_offset = 0,
  36396     } })));
  36397 }
  36398 
  36399 fn resolveAddressSpace(
  36400     sema: *Sema,
  36401     block: *Block,
  36402     src: LazySrcLoc,
  36403     zir_ref: Zir.Inst.Ref,
  36404     ctx: std.builtin.AddressSpace.Context,
  36405 ) !std.builtin.AddressSpace {
  36406     const air_ref = try sema.resolveInst(zir_ref);
  36407     return sema.analyzeAsAddressSpace(block, src, air_ref, ctx);
  36408 }
  36409 
  36410 pub fn analyzeAsAddressSpace(
  36411     sema: *Sema,
  36412     block: *Block,
  36413     src: LazySrcLoc,
  36414     air_ref: Air.Inst.Ref,
  36415     ctx: std.builtin.AddressSpace.Context,
  36416 ) !std.builtin.AddressSpace {
  36417     const pt = sema.pt;
  36418     const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace);
  36419     const coerced = try sema.coerce(block, addrspace_ty, air_ref, src);
  36420     const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{ .simple = .@"addrspace" });
  36421     const address_space = try sema.interpretBuiltinType(block, src, addrspace_val, std.builtin.AddressSpace);
  36422     const target = pt.zcu.getTarget();
  36423 
  36424     if (!target.cpu.supportsAddressSpace(address_space, ctx)) {
  36425         // TODO error messages could be made more elaborate here
  36426         const entity = switch (ctx) {
  36427             .function => "functions",
  36428             .variable => "mutable values",
  36429             .constant => "constant values",
  36430             .pointer => "pointers",
  36431         };
  36432         return sema.fail(
  36433             block,
  36434             src,
  36435             "{s} with address space '{s}' are not supported on {s}",
  36436             .{ entity, @tagName(address_space), @tagName(target.cpu.arch.family()) },
  36437         );
  36438     }
  36439 
  36440     return address_space;
  36441 }
  36442 
  36443 /// Asserts the value is a pointer and dereferences it.
  36444 /// Returns `null` if the pointer contents cannot be loaded at comptime.
  36445 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
  36446     // TODO: audit use sites to eliminate this coercion
  36447     const pt = sema.pt;
  36448     const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty);
  36449     switch (try sema.pointerDerefExtra(block, src, coerced_ptr_val)) {
  36450         .runtime_load => return null,
  36451         .val => |v| return v,
  36452         .needed_well_defined => |ty| return sema.fail(
  36453             block,
  36454             src,
  36455             "comptime dereference requires '{f}' to have a well-defined layout",
  36456             .{ty.fmt(pt)},
  36457         ),
  36458         .out_of_bounds => |ty| return sema.fail(
  36459             block,
  36460             src,
  36461             "dereference of '{f}' exceeds bounds of containing decl of type '{f}'",
  36462             .{ ptr_ty.fmt(pt), ty.fmt(pt) },
  36463         ),
  36464     }
  36465 }
  36466 
  36467 const DerefResult = union(enum) {
  36468     runtime_load,
  36469     val: Value,
  36470     needed_well_defined: Type,
  36471     out_of_bounds: Type,
  36472 };
  36473 
  36474 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value) CompileError!DerefResult {
  36475     const pt = sema.pt;
  36476     const ip = &pt.zcu.intern_pool;
  36477     switch (try sema.loadComptimePtr(block, src, ptr_val)) {
  36478         .success => |mv| return .{ .val = try mv.intern(pt, sema.arena) },
  36479         .runtime_load => return .runtime_load,
  36480         .undef => return sema.failWithUseOfUndef(block, src, null),
  36481         .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}),
  36482         .null_payload => return sema.fail(block, src, "attempt to use null value", .{}),
  36483         .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}),
  36484         .needed_well_defined => |ty| return .{ .needed_well_defined = ty },
  36485         .out_of_bounds => |ty| return .{ .out_of_bounds = ty },
  36486         .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}),
  36487     }
  36488 }
  36489 
  36490 /// Used to convert a u64 value to a usize value, emitting a compile error if the number
  36491 /// is too big to fit.
  36492 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {
  36493     if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int;
  36494     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});
  36495 }
  36496 
  36497 /// For pointer-like optionals, it returns the pointer type. For pointers,
  36498 /// the type is returned unmodified.
  36499 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether
  36500 /// a type has zero bits, which can cause a "foo depends on itself" compile error.
  36501 /// This logic must be kept in sync with `Type.isPtrLikeOptional`.
  36502 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type {
  36503     const pt = sema.pt;
  36504     const zcu = pt.zcu;
  36505     return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
  36506         .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  36507             .one, .many, .c => ty,
  36508             .slice => null,
  36509         },
  36510         .opt_type => |opt_child| switch (zcu.intern_pool.indexToKey(opt_child)) {
  36511             .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  36512                 .slice, .c => null,
  36513                 .many, .one => {
  36514                     if (ptr_type.flags.is_allowzero) return null;
  36515 
  36516                     // optionals of zero sized types behave like bools, not pointers
  36517                     const payload_ty: Type = .fromInterned(opt_child);
  36518                     if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) {
  36519                         return null;
  36520                     }
  36521 
  36522                     return payload_ty;
  36523                 },
  36524             },
  36525             else => null,
  36526         },
  36527         else => null,
  36528     };
  36529 }
  36530 
  36531 fn unionFieldIndex(
  36532     sema: *Sema,
  36533     block: *Block,
  36534     union_ty: Type,
  36535     field_name: InternPool.NullTerminatedString,
  36536     field_src: LazySrcLoc,
  36537 ) !u32 {
  36538     const pt = sema.pt;
  36539     const zcu = pt.zcu;
  36540     const ip = &zcu.intern_pool;
  36541     try union_ty.resolveFields(pt);
  36542     const union_obj = zcu.typeToUnion(union_ty).?;
  36543     const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse
  36544         return sema.failWithBadUnionFieldAccess(block, union_ty, union_obj, field_src, field_name);
  36545     return @intCast(field_index);
  36546 }
  36547 
  36548 fn structFieldIndex(
  36549     sema: *Sema,
  36550     block: *Block,
  36551     struct_ty: Type,
  36552     field_name: InternPool.NullTerminatedString,
  36553     field_src: LazySrcLoc,
  36554 ) !u32 {
  36555     const pt = sema.pt;
  36556     const zcu = pt.zcu;
  36557     const ip = &zcu.intern_pool;
  36558     try struct_ty.resolveFields(pt);
  36559     const struct_type = zcu.typeToStruct(struct_ty).?;
  36560     return struct_type.nameIndex(ip, field_name) orelse
  36561         return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name);
  36562 }
  36563 
  36564 const IntFromFloatMode = enum { exact, truncate };
  36565 
  36566 fn intFromFloat(
  36567     sema: *Sema,
  36568     block: *Block,
  36569     src: LazySrcLoc,
  36570     val: Value,
  36571     float_ty: Type,
  36572     int_ty: Type,
  36573     mode: IntFromFloatMode,
  36574 ) CompileError!Value {
  36575     const pt = sema.pt;
  36576     const zcu = pt.zcu;
  36577     if (float_ty.zigTypeTag(zcu) == .vector) {
  36578         const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(zcu));
  36579         for (result_data, 0..) |*scalar, elem_idx| {
  36580             const elem_val = try val.elemValue(pt, elem_idx);
  36581             scalar.* = (try sema.intFromFloatScalar(block, src, elem_val, int_ty.scalarType(zcu), mode, elem_idx)).toIntern();
  36582         }
  36583         return pt.aggregateValue(int_ty, result_data);
  36584     }
  36585     return sema.intFromFloatScalar(block, src, val, int_ty, mode, null);
  36586 }
  36587 
  36588 fn intFromFloatScalar(
  36589     sema: *Sema,
  36590     block: *Block,
  36591     src: LazySrcLoc,
  36592     val: Value,
  36593     int_ty: Type,
  36594     mode: IntFromFloatMode,
  36595     vec_idx: ?usize,
  36596 ) CompileError!Value {
  36597     const pt = sema.pt;
  36598     const zcu = pt.zcu;
  36599 
  36600     if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, vec_idx);
  36601 
  36602     const float = val.toFloat(f128, zcu);
  36603     if (std.math.isNan(float)) {
  36604         return sema.fail(block, src, "float value NaN cannot be stored in integer type '{f}'", .{
  36605             int_ty.fmt(pt),
  36606         });
  36607     }
  36608     if (std.math.isInf(float)) {
  36609         return sema.fail(block, src, "float value Inf cannot be stored in integer type '{f}'", .{
  36610             int_ty.fmt(pt),
  36611         });
  36612     }
  36613 
  36614     var big_int: std.math.big.int.Mutable = .{
  36615         .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)),
  36616         .len = undefined,
  36617         .positive = undefined,
  36618     };
  36619     switch (big_int.setFloat(float, .trunc)) {
  36620         .inexact => switch (mode) {
  36621             .exact => return sema.fail(
  36622                 block,
  36623                 src,
  36624                 "fractional component prevents float value '{f}' from coercion to type '{f}'",
  36625                 .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) },
  36626             ),
  36627             .truncate => {},
  36628         },
  36629         .exact => {},
  36630     }
  36631     const cti_result = try pt.intValue_big(.comptime_int, big_int.toConst());
  36632     if (int_ty.toIntern() == .comptime_int_type) return cti_result;
  36633 
  36634     const int_info = int_ty.intInfo(zcu);
  36635     if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) {
  36636         return sema.fail(block, src, "float value '{f}' cannot be stored in integer type '{f}'", .{
  36637             val.fmtValueSema(pt, sema), int_ty.fmt(pt),
  36638         });
  36639     }
  36640     return pt.getCoerced(cti_result, int_ty);
  36641 }
  36642 
  36643 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
  36644 /// Vectors are also accepted. Vector results are reduced with AND.
  36645 ///
  36646 /// If provided, `vector_index` reports the first element that failed the range check.
  36647 fn intFitsInType(
  36648     sema: *Sema,
  36649     val: Value,
  36650     ty: Type,
  36651     vector_index: ?*usize,
  36652 ) CompileError!bool {
  36653     const pt = sema.pt;
  36654     const zcu = pt.zcu;
  36655     if (ty.toIntern() == .comptime_int_type) return true;
  36656     const info = ty.intInfo(zcu);
  36657     switch (val.toIntern()) {
  36658         .zero_usize, .zero_u8 => return true,
  36659         else => switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  36660             .undef => return true,
  36661             .variable, .@"extern", .func, .ptr => {
  36662                 const target = zcu.getTarget();
  36663                 const ptr_bits = target.ptrBitWidth();
  36664                 return switch (info.signedness) {
  36665                     .signed => info.bits > ptr_bits,
  36666                     .unsigned => info.bits >= ptr_bits,
  36667                 };
  36668             },
  36669             .int => |int| switch (int.storage) {
  36670                 .u64, .i64, .big_int => {
  36671                     var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  36672                     const big_int = int.storage.toBigInt(&buffer);
  36673                     return big_int.fitsInTwosComp(info.signedness, info.bits);
  36674                 },
  36675                 .lazy_align => |lazy_ty| {
  36676                     const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed);
  36677                     // If it is u16 or bigger we know the alignment fits without resolving it.
  36678                     if (info.bits >= max_needed_bits) return true;
  36679                     const x = try Type.fromInterned(lazy_ty).abiAlignmentSema(pt);
  36680                     if (x == .none) return true;
  36681                     const actual_needed_bits = @as(usize, x.toLog2Units()) + 1 + @intFromBool(info.signedness == .signed);
  36682                     return info.bits >= actual_needed_bits;
  36683                 },
  36684                 .lazy_size => |lazy_ty| {
  36685                     const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed);
  36686                     // If it is u64 or bigger we know the size fits without resolving it.
  36687                     if (info.bits >= max_needed_bits) return true;
  36688                     const x = try Type.fromInterned(lazy_ty).abiSizeSema(pt);
  36689                     if (x == 0) return true;
  36690                     const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed);
  36691                     return info.bits >= actual_needed_bits;
  36692                 },
  36693             },
  36694             .aggregate => |aggregate| {
  36695                 assert(ty.zigTypeTag(zcu) == .vector);
  36696                 return switch (aggregate.storage) {
  36697                     .bytes => |bytes| for (bytes.toSlice(ty.vectorLen(zcu), &zcu.intern_pool), 0..) |byte, i| {
  36698                         if (byte == 0) continue;
  36699                         const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed);
  36700                         if (info.bits >= actual_needed_bits) continue;
  36701                         if (vector_index) |vi| vi.* = i;
  36702                         break false;
  36703                     } else true,
  36704                     .elems, .repeated_elem => for (switch (aggregate.storage) {
  36705                         .bytes => unreachable,
  36706                         .elems => |elems| elems,
  36707                         .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem),
  36708                     }, 0..) |elem, i| {
  36709                         if (try sema.intFitsInType(Value.fromInterned(elem), ty.scalarType(zcu), null)) continue;
  36710                         if (vector_index) |vi| vi.* = i;
  36711                         break false;
  36712                     } else true,
  36713                 };
  36714             },
  36715             else => unreachable,
  36716         },
  36717     }
  36718 }
  36719 
  36720 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
  36721     const pt = sema.pt;
  36722     if (!(try int_val.compareAllWithZeroSema(.gte, pt))) return false;
  36723     const end_val = try pt.intValue(tag_ty, end);
  36724     if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
  36725     return true;
  36726 }
  36727 
  36728 /// Asserts the type is an enum.
  36729 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
  36730     const pt = sema.pt;
  36731     const zcu = pt.zcu;
  36732     const enum_type = zcu.intern_pool.loadEnumType(ty.toIntern());
  36733     assert(enum_type.tag_mode != .nonexhaustive);
  36734     // The `tagValueIndex` function call below relies on the type being the integer tag type.
  36735     // `getCoerced` assumes the value will fit the new type.
  36736     if (!(try sema.intFitsInType(int, .fromInterned(enum_type.tag_ty), null))) return false;
  36737     const int_coerced = try pt.getCoerced(int, .fromInterned(enum_type.tag_ty));
  36738 
  36739     return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null;
  36740 }
  36741 
  36742 /// Asserts the values are comparable. Both operands have type `ty`.
  36743 /// For vectors, returns true if the comparison is true for ALL elements.
  36744 ///
  36745 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)`
  36746 fn compareAll(
  36747     sema: *Sema,
  36748     lhs: Value,
  36749     op: std.math.CompareOperator,
  36750     rhs: Value,
  36751     ty: Type,
  36752 ) CompileError!bool {
  36753     const pt = sema.pt;
  36754     const zcu = pt.zcu;
  36755     if (ty.zigTypeTag(zcu) == .vector) {
  36756         var i: usize = 0;
  36757         while (i < ty.vectorLen(zcu)) : (i += 1) {
  36758             const lhs_elem = try lhs.elemValue(pt, i);
  36759             const rhs_elem = try rhs.elemValue(pt, i);
  36760             if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu)))) {
  36761                 return false;
  36762             }
  36763         }
  36764         return true;
  36765     }
  36766     return sema.compareScalar(lhs, op, rhs, ty);
  36767 }
  36768 
  36769 /// Asserts the values are comparable. Both operands have type `ty`.
  36770 fn compareScalar(
  36771     sema: *Sema,
  36772     lhs: Value,
  36773     op: std.math.CompareOperator,
  36774     rhs: Value,
  36775     ty: Type,
  36776 ) CompileError!bool {
  36777     const pt = sema.pt;
  36778     const coerced_lhs = try pt.getCoerced(lhs, ty);
  36779     const coerced_rhs = try pt.getCoerced(rhs, ty);
  36780 
  36781     // Equality comparisons of signed zero and NaN need to use floating point semantics
  36782     if (coerced_lhs.isFloat(pt.zcu) or coerced_rhs.isFloat(pt.zcu))
  36783         return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt);
  36784 
  36785     switch (op) {
  36786         .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty),
  36787         .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)),
  36788         else => return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt),
  36789     }
  36790 }
  36791 
  36792 fn valuesEqual(
  36793     sema: *Sema,
  36794     lhs: Value,
  36795     rhs: Value,
  36796     ty: Type,
  36797 ) CompileError!bool {
  36798     return lhs.eql(rhs, ty, sema.pt.zcu);
  36799 }
  36800 
  36801 /// Asserts the values are comparable vectors of type `ty`.
  36802 fn compareVector(
  36803     sema: *Sema,
  36804     lhs: Value,
  36805     op: std.math.CompareOperator,
  36806     rhs: Value,
  36807     ty: Type,
  36808 ) !Value {
  36809     const pt = sema.pt;
  36810     const zcu = pt.zcu;
  36811     assert(ty.zigTypeTag(zcu) == .vector);
  36812     const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(zcu));
  36813     for (result_data, 0..) |*scalar, i| {
  36814         const lhs_elem = try lhs.elemValue(pt, i);
  36815         const rhs_elem = try rhs.elemValue(pt, i);
  36816         if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) {
  36817             scalar.* = .undef_bool;
  36818         } else {
  36819             const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu));
  36820             scalar.* = Value.makeBool(res_bool).toIntern();
  36821         }
  36822     }
  36823     return pt.aggregateValue(try pt.vectorType(.{
  36824         .len = ty.vectorLen(zcu),
  36825         .child = .bool_type,
  36826     }), result_data);
  36827 }
  36828 
  36829 /// Merge lhs with rhs.
  36830 /// Asserts that lhs and rhs are both error sets and are resolved.
  36831 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {
  36832     const pt = sema.pt;
  36833     const ip = &pt.zcu.intern_pool;
  36834     const arena = sema.arena;
  36835     const lhs_names = lhs.errorSetNames(pt.zcu);
  36836     const rhs_names = rhs.errorSetNames(pt.zcu);
  36837     var names: InferredErrorSet.NameMap = .{};
  36838     try names.ensureUnusedCapacity(arena, lhs_names.len);
  36839 
  36840     for (0..lhs_names.len) |lhs_index| {
  36841         names.putAssumeCapacityNoClobber(lhs_names.get(ip)[lhs_index], {});
  36842     }
  36843     for (0..rhs_names.len) |rhs_index| {
  36844         try names.put(arena, rhs_names.get(ip)[rhs_index], {});
  36845     }
  36846 
  36847     return pt.errorSetFromUnsortedNames(names.keys());
  36848 }
  36849 
  36850 /// Avoids crashing the compiler when asking if inferred allocations are noreturn.
  36851 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool {
  36852     if (ref == .unreachable_value) return true;
  36853     if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) {
  36854         .inferred_alloc, .inferred_alloc_comptime => return false,
  36855         else => {},
  36856     };
  36857     return sema.typeOf(ref).isNoReturn(sema.pt.zcu);
  36858 }
  36859 
  36860 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type.
  36861 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool {
  36862     if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) {
  36863         .inferred_alloc, .inferred_alloc_comptime => return false,
  36864         else => {},
  36865     };
  36866     return sema.typeOf(ref).zigTypeTag(sema.pt.zcu) == tag;
  36867 }
  36868 
  36869 pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
  36870     const pt = sema.pt;
  36871     if (!pt.zcu.comp.incremental) return;
  36872 
  36873     const gop = try sema.dependencies.getOrPut(sema.gpa, dependee);
  36874     if (gop.found_existing) return;
  36875 
  36876     // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields
  36877     // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would
  36878     // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve
  36879     // the loop.
  36880     // Note that this also disallows a `nav_val`
  36881     switch (sema.owner.unwrap()) {
  36882         .nav_val => |this_nav| switch (dependee) {
  36883             .nav_val => |other_nav| if (this_nav == other_nav) return,
  36884             else => {},
  36885         },
  36886         .nav_ty => |this_nav| switch (dependee) {
  36887             .nav_ty => |other_nav| if (this_nav == other_nav) return,
  36888             else => {},
  36889         },
  36890         else => {},
  36891     }
  36892 
  36893     try pt.addDependency(sema.owner, dependee);
  36894 }
  36895 
  36896 fn isComptimeMutablePtr(sema: *Sema, val: Value) bool {
  36897     return switch (sema.pt.zcu.intern_pool.indexToKey(val.toIntern())) {
  36898         .slice => |slice| sema.isComptimeMutablePtr(Value.fromInterned(slice.ptr)),
  36899         .ptr => |ptr| switch (ptr.base_addr) {
  36900             .uav, .nav, .int => false,
  36901             .comptime_field => true,
  36902             .comptime_alloc => |alloc_index| !sema.getComptimeAlloc(alloc_index).is_const,
  36903             .eu_payload, .opt_payload => |base| sema.isComptimeMutablePtr(Value.fromInterned(base)),
  36904             .arr_elem, .field => |bi| sema.isComptimeMutablePtr(Value.fromInterned(bi.base)),
  36905         },
  36906         else => false,
  36907     };
  36908 }
  36909 
  36910 fn checkRuntimeValue(sema: *Sema, ptr: Air.Inst.Ref) bool {
  36911     const val = ptr.toInterned() orelse return true;
  36912     return !Value.fromInterned(val).canMutateComptimeVarState(sema.pt.zcu);
  36913 }
  36914 
  36915 fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Air.Inst.Ref) CompileError!void {
  36916     if (sema.checkRuntimeValue(val)) return;
  36917     return sema.failWithOwnedErrorMsg(block, msg: {
  36918         const msg = try sema.errMsg(val_src, "runtime value contains reference to comptime var", .{});
  36919         errdefer msg.destroy(sema.gpa);
  36920         try sema.errNote(val_src, msg, "comptime var pointers are not available at runtime", .{});
  36921         const pt = sema.pt;
  36922         const zcu = pt.zcu;
  36923         const val_str = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, "runtime_value", .no_embedded_nulls);
  36924         try sema.explainWhyValueContainsReferenceToComptimeVar(msg, val_src, val_str, .fromInterned(val.toInterned().?));
  36925         break :msg msg;
  36926     });
  36927 }
  36928 
  36929 fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError {
  36930     return sema.failWithOwnedErrorMsg(block, msg: {
  36931         const msg = try sema.errMsg(src, "{s} contains reference to comptime var", .{kind_of_value});
  36932         errdefer msg.destroy(sema.gpa);
  36933         if (val) |v| try sema.explainWhyValueContainsReferenceToComptimeVar(msg, src, value_name, v);
  36934         break :msg msg;
  36935     });
  36936 }
  36937 
  36938 fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, val: Value) Allocator.Error!void {
  36939     // Our goal is something like this:
  36940     //   note: '(value.? catch unreachable)[0]' points to 'v0.?.foo'
  36941     //   note: '(v0.?.bar catch unreachable)' points to 'v1'
  36942     //   note: 'v1.?' points to a comptime var
  36943 
  36944     var intermediate_value_count: u32 = 0;
  36945     var cur_val: Value = val;
  36946     while (true) {
  36947         switch (try sema.notePathToComptimeAllocPtr(msg, src, cur_val, intermediate_value_count, value_name)) {
  36948             .done => return,
  36949             .new_val => |new_val| {
  36950                 intermediate_value_count += 1;
  36951                 cur_val = new_val;
  36952             },
  36953         }
  36954     }
  36955 }
  36956 
  36957 fn notePathToComptimeAllocPtr(
  36958     sema: *Sema,
  36959     msg: *Zcu.ErrorMsg,
  36960     src: LazySrcLoc,
  36961     val: Value,
  36962     intermediate_value_count: u32,
  36963     start_value_name: InternPool.NullTerminatedString,
  36964 ) Allocator.Error!union(enum) {
  36965     done,
  36966     new_val: Value,
  36967 } {
  36968     const arena = sema.arena;
  36969     const pt = sema.pt;
  36970     const zcu = pt.zcu;
  36971     const ip = &zcu.intern_pool;
  36972 
  36973     var first_path: std.ArrayListUnmanaged(u8) = .empty;
  36974     if (intermediate_value_count == 0) {
  36975         try first_path.print(arena, "{f}", .{start_value_name.fmt(ip)});
  36976     } else {
  36977         try first_path.print(arena, "v{d}", .{intermediate_value_count - 1});
  36978     }
  36979 
  36980     const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path);
  36981 
  36982     switch (ip.indexToKey(comptime_ptr.toIntern()).ptr.base_addr) {
  36983         .comptime_field => {
  36984             try sema.errNote(src, msg, "'{s}' points to comptime field", .{first_path.items});
  36985             return .done;
  36986         },
  36987         .comptime_alloc => |idx| {
  36988             const cta = sema.getComptimeAlloc(idx);
  36989             if (!cta.is_const) {
  36990                 try sema.errNote(cta.src, msg, "'{s}' points to comptime var declared here", .{first_path.items});
  36991                 return .done;
  36992             }
  36993         },
  36994         else => {}, // there will be another stage
  36995     }
  36996 
  36997     const derivation = comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema) catch |err| switch (err) {
  36998         error.OutOfMemory => |e| return e,
  36999         error.AnalysisFail => unreachable,
  37000     };
  37001 
  37002     var second_path_aw: std.Io.Writer.Allocating = .init(arena);
  37003     defer second_path_aw.deinit();
  37004     const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count});
  37005     const deriv_start = @import("print_value.zig").printPtrDerivation(
  37006         derivation,
  37007         &second_path_aw.writer,
  37008         pt,
  37009         .lvalue,
  37010         .{ .str = inter_name },
  37011         20,
  37012     ) catch return error.OutOfMemory;
  37013 
  37014     switch (deriv_start) {
  37015         .int, .nav_ptr => unreachable,
  37016         .uav_ptr => |uav| {
  37017             try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() });
  37018             return .{ .new_val = .fromInterned(uav.val) };
  37019         },
  37020         .comptime_alloc_ptr => |cta_info| {
  37021             try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() });
  37022             const cta = sema.getComptimeAlloc(cta_info.idx);
  37023             if (cta.is_const) {
  37024                 return .{ .new_val = cta_info.val };
  37025             } else {
  37026                 try sema.errNote(cta.src, msg, "'{s}' is a comptime var declared here", .{inter_name});
  37027                 return .done;
  37028             }
  37029         },
  37030         .comptime_field_ptr => {
  37031             try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() });
  37032             try sema.errNote(src, msg, "'{s}' is a comptime field", .{inter_name});
  37033             return .done;
  37034         },
  37035         .eu_payload_ptr,
  37036         .opt_payload_ptr,
  37037         .field_ptr,
  37038         .elem_ptr,
  37039         .offset_and_cast,
  37040         => unreachable,
  37041     }
  37042 }
  37043 
  37044 fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayListUnmanaged(u8)) Allocator.Error!Value {
  37045     const pt = sema.pt;
  37046     const zcu = pt.zcu;
  37047     const ip = &zcu.intern_pool;
  37048     const arena = sema.arena;
  37049     assert(val.canMutateComptimeVarState(zcu));
  37050     switch (ip.indexToKey(val.toIntern())) {
  37051         .ptr => return val,
  37052         .error_union => |eu| {
  37053             try path.insert(arena, 0, '(');
  37054             try path.appendSlice(arena, " catch unreachable)");
  37055             return sema.notePathToComptimeAllocPtrInner(.fromInterned(eu.val.payload), path);
  37056         },
  37057         .slice => |slice| {
  37058             try path.appendSlice(arena, ".ptr");
  37059             return sema.notePathToComptimeAllocPtrInner(.fromInterned(slice.ptr), path);
  37060         },
  37061         .opt => |opt| {
  37062             try path.appendSlice(arena, ".?");
  37063             return sema.notePathToComptimeAllocPtrInner(.fromInterned(opt.val), path);
  37064         },
  37065         .un => |un| {
  37066             assert(un.tag != .none);
  37067             const union_ty: Type = .fromInterned(un.ty);
  37068             const backing_enum = union_ty.unionTagTypeHypothetical(zcu);
  37069             const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?;
  37070             const field_name = backing_enum.enumFieldName(field_idx, zcu);
  37071             try path.print(arena, ".{f}", .{field_name.fmt(ip)});
  37072             return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path);
  37073         },
  37074         .aggregate => |agg| {
  37075             const elem: InternPool.Index, const elem_idx: usize = switch (agg.storage) {
  37076                 .bytes => unreachable,
  37077                 .repeated_elem => |elem| .{ elem, 0 },
  37078                 .elems => |elems| for (elems, 0..) |elem, elem_idx| {
  37079                     if (Value.fromInterned(elem).canMutateComptimeVarState(zcu)) {
  37080                         break .{ elem, elem_idx };
  37081                     }
  37082                 } else unreachable,
  37083             };
  37084             const agg_ty: Type = .fromInterned(agg.ty);
  37085             switch (agg_ty.zigTypeTag(zcu)) {
  37086                 .array, .vector => try path.print(arena, "[{d}]", .{elem_idx}),
  37087                 .pointer => switch (elem_idx) {
  37088                     Value.slice_ptr_index => try path.appendSlice(arena, ".ptr"),
  37089                     Value.slice_len_index => try path.appendSlice(arena, ".len"),
  37090                     else => unreachable,
  37091                 },
  37092                 .@"struct" => if (agg_ty.isTuple(zcu)) {
  37093                     try path.print(arena, "[{d}]", .{elem_idx});
  37094                 } else {
  37095                     const name = agg_ty.structFieldName(elem_idx, zcu).unwrap().?;
  37096                     try path.print(arena, ".{f}", .{name.fmt(ip)});
  37097                 },
  37098                 else => unreachable,
  37099             }
  37100             return sema.notePathToComptimeAllocPtrInner(.fromInterned(elem), path);
  37101         },
  37102         else => unreachable,
  37103     }
  37104 }
  37105 
  37106 /// Returns true if any value contained in `val` is undefined.
  37107 fn anyUndef(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) !bool {
  37108     const pt = sema.pt;
  37109     const zcu = pt.zcu;
  37110     return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  37111         .undef => true,
  37112         .simple_value => |v| v == .undefined,
  37113         .slice => {
  37114             // If the slice contents are runtime-known, reification will fail later on with a
  37115             // specific error message.
  37116             const arr = try sema.maybeDerefSliceAsArray(block, src, val) orelse return false;
  37117             return sema.anyUndef(block, src, arr);
  37118         },
  37119         .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| {
  37120             const elem = zcu.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i];
  37121             if (try sema.anyUndef(block, src, Value.fromInterned(elem))) break true;
  37122         } else false,
  37123         else => false,
  37124     };
  37125 }
  37126 
  37127 /// Asserts that `slice_val` is a slice of `u8`.
  37128 fn sliceToIpString(
  37129     sema: *Sema,
  37130     block: *Block,
  37131     src: LazySrcLoc,
  37132     slice_val: Value,
  37133     reason: ComptimeReason,
  37134 ) CompileError!InternPool.NullTerminatedString {
  37135     const pt = sema.pt;
  37136     const zcu = pt.zcu;
  37137     const slice_ty = slice_val.typeOf(zcu);
  37138     assert(slice_ty.isSlice(zcu));
  37139     assert(slice_ty.childType(zcu).toIntern() == .u8_type);
  37140     const array_val = try sema.derefSliceAsArray(block, src, slice_val, reason);
  37141     const array_ty = array_val.typeOf(zcu);
  37142     return array_val.toIpString(array_ty, pt);
  37143 }
  37144 
  37145 /// Given a slice value, attempts to dereference it into a comptime-known array.
  37146 /// Emits a compile error if the contents of the slice are not comptime-known.
  37147 /// Asserts that `slice_val` is a slice.
  37148 fn derefSliceAsArray(
  37149     sema: *Sema,
  37150     block: *Block,
  37151     src: LazySrcLoc,
  37152     slice_val: Value,
  37153     reason: ComptimeReason,
  37154 ) CompileError!Value {
  37155     return try sema.maybeDerefSliceAsArray(block, src, slice_val) orelse {
  37156         return sema.failWithNeededComptime(block, src, reason);
  37157     };
  37158 }
  37159 
  37160 /// Given a slice value, attempts to dereference it into a comptime-known array.
  37161 /// Returns `null` if the contents of the slice are not comptime-known.
  37162 /// Asserts that `slice_val` is a slice.
  37163 fn maybeDerefSliceAsArray(
  37164     sema: *Sema,
  37165     block: *Block,
  37166     src: LazySrcLoc,
  37167     slice_val: Value,
  37168 ) CompileError!?Value {
  37169     const pt = sema.pt;
  37170     const zcu = pt.zcu;
  37171     const ip = &zcu.intern_pool;
  37172     assert(slice_val.typeOf(zcu).isSlice(zcu));
  37173     const slice = switch (ip.indexToKey(slice_val.toIntern())) {
  37174         .undef => return sema.failWithUseOfUndef(block, src, null),
  37175         .slice => |slice| slice,
  37176         else => unreachable,
  37177     };
  37178     const elem_ty = Type.fromInterned(slice.ty).childType(zcu);
  37179     const len = try Value.fromInterned(slice.len).toUnsignedIntSema(pt);
  37180     const array_ty = try pt.arrayType(.{
  37181         .child = elem_ty.toIntern(),
  37182         .len = len,
  37183     });
  37184     const ptr_ty = try pt.ptrTypeSema(p: {
  37185         var p = Type.fromInterned(slice.ty).ptrInfo(zcu);
  37186         p.flags.size = .one;
  37187         p.child = array_ty.toIntern();
  37188         p.sentinel = .none;
  37189         break :p p;
  37190     });
  37191     const casted_ptr = try pt.getCoerced(Value.fromInterned(slice.ptr), ptr_ty);
  37192     return sema.pointerDeref(block, src, casted_ptr, ptr_ty);
  37193 }
  37194 
  37195 fn analyzeUnreachable(sema: *Sema, block: *Block, src: LazySrcLoc, safety_check: bool) !void {
  37196     if (safety_check and block.wantSafety()) {
  37197         // We only apply the first hint in a branch.
  37198         // This allows user-provided hints to override implicit cold hints.
  37199         if (sema.branch_hint == null) {
  37200             sema.branch_hint = .cold;
  37201         }
  37202 
  37203         try sema.safetyPanic(block, src, .reached_unreachable);
  37204     } else {
  37205         _ = try block.addNoOp(.unreach);
  37206     }
  37207 }
  37208 
  37209 /// This should be called exactly once, at the end of a `Sema`'s lifetime.
  37210 /// It takes the exports stored in `sema.export` and flushes them to the `Zcu`
  37211 /// to be processed by the linker after the update.
  37212 pub fn flushExports(sema: *Sema) !void {
  37213     if (sema.exports.items.len == 0) return;
  37214 
  37215     const zcu = sema.pt.zcu;
  37216     const gpa = zcu.gpa;
  37217 
  37218     // There may be existing exports. For instance, a struct may export
  37219     // things during both field type resolution and field default resolution.
  37220     //
  37221     // So, pick up and delete any existing exports. This strategy performs
  37222     // redundant work, but that's okay, because this case is exceedingly rare.
  37223     if (zcu.single_exports.get(sema.owner)) |export_idx| {
  37224         try sema.exports.append(gpa, export_idx.ptr(zcu).*);
  37225     } else if (zcu.multi_exports.get(sema.owner)) |info| {
  37226         try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]);
  37227     }
  37228     zcu.deleteUnitExports(sema.owner);
  37229 
  37230     // `sema.exports` is completed; store the data into the `Zcu`.
  37231     if (sema.exports.items.len == 1) {
  37232         try zcu.single_exports.ensureUnusedCapacity(gpa, 1);
  37233         const export_idx: Zcu.Export.Index = zcu.free_exports.pop() orelse idx: {
  37234             _ = try zcu.all_exports.addOne(gpa);
  37235             break :idx @enumFromInt(zcu.all_exports.items.len - 1);
  37236         };
  37237         export_idx.ptr(zcu).* = sema.exports.items[0];
  37238         zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx);
  37239     } else {
  37240         try zcu.multi_exports.ensureUnusedCapacity(gpa, 1);
  37241         const exports_base = zcu.all_exports.items.len;
  37242         try zcu.all_exports.appendSlice(gpa, sema.exports.items);
  37243         zcu.multi_exports.putAssumeCapacityNoClobber(sema.owner, .{
  37244             .index = @intCast(exports_base),
  37245             .len = @intCast(sema.exports.items.len),
  37246         });
  37247     }
  37248 }
  37249 
  37250 /// Called as soon as a `declared` enum type is created.
  37251 /// Resolves the tag type and field inits.
  37252 /// Marks the `src_inst` dependency on the enum's declaration, so call sites need not do this.
  37253 pub fn resolveDeclaredEnum(
  37254     pt: Zcu.PerThread,
  37255     wip_ty: InternPool.WipEnumType,
  37256     inst: Zir.Inst.Index,
  37257     tracked_inst: InternPool.TrackedInst.Index,
  37258     namespace: InternPool.NamespaceIndex,
  37259     type_name: InternPool.NullTerminatedString,
  37260     small: Zir.Inst.EnumDecl.Small,
  37261     body: []const Zir.Inst.Index,
  37262     tag_type_ref: Zir.Inst.Ref,
  37263     any_values: bool,
  37264     fields_len: u32,
  37265     zir: Zir,
  37266     body_end: usize,
  37267 ) Zcu.SemaError!void {
  37268     const zcu = pt.zcu;
  37269     const gpa = zcu.gpa;
  37270 
  37271     const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
  37272 
  37273     var arena: std.heap.ArenaAllocator = .init(gpa);
  37274     defer arena.deinit();
  37275 
  37276     var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa);
  37277     defer comptime_err_ret_trace.deinit();
  37278 
  37279     var sema: Sema = .{
  37280         .pt = pt,
  37281         .gpa = gpa,
  37282         .arena = arena.allocator(),
  37283         .code = zir,
  37284         .owner = .wrap(.{ .type = wip_ty.index }),
  37285         .func_index = .none,
  37286         .func_is_naked = false,
  37287         .fn_ret_ty = .void,
  37288         .fn_ret_ty_ies = null,
  37289         .comptime_err_ret_trace = &comptime_err_ret_trace,
  37290     };
  37291     defer sema.deinit();
  37292 
  37293     if (zcu.comp.debugIncremental()) {
  37294         const info = try zcu.incremental_debug_state.getUnitInfo(gpa, sema.owner);
  37295         info.last_update_gen = zcu.generation;
  37296     }
  37297 
  37298     try sema.declareDependency(.{ .src_hash = tracked_inst });
  37299 
  37300     var block: Block = .{
  37301         .parent = null,
  37302         .sema = &sema,
  37303         .namespace = namespace,
  37304         .instructions = .{},
  37305         .inlining = null,
  37306         .comptime_reason = .{ .reason = .{
  37307             .src = src,
  37308             .r = .{ .simple = .enum_fields },
  37309         } },
  37310         .src_base_inst = tracked_inst,
  37311         .type_name_ctx = type_name,
  37312     };
  37313     defer block.instructions.deinit(gpa);
  37314 
  37315     sema.resolveDeclaredEnumInner(
  37316         &block,
  37317         wip_ty,
  37318         inst,
  37319         tracked_inst,
  37320         src,
  37321         small,
  37322         body,
  37323         tag_type_ref,
  37324         any_values,
  37325         fields_len,
  37326         zir,
  37327         body_end,
  37328     ) catch |err| switch (err) {
  37329         error.ComptimeBreak => unreachable,
  37330         error.ComptimeReturn => unreachable,
  37331         error.OutOfMemory => |e| return e,
  37332         error.AnalysisFail => {
  37333             if (!zcu.failed_analysis.contains(sema.owner)) {
  37334                 try zcu.transitive_failed_analysis.put(gpa, sema.owner, {});
  37335             }
  37336             return error.AnalysisFail;
  37337         },
  37338     };
  37339 }
  37340 
  37341 fn resolveDeclaredEnumInner(
  37342     sema: *Sema,
  37343     block: *Block,
  37344     wip_ty: InternPool.WipEnumType,
  37345     inst: Zir.Inst.Index,
  37346     tracked_inst: InternPool.TrackedInst.Index,
  37347     src: LazySrcLoc,
  37348     small: Zir.Inst.EnumDecl.Small,
  37349     body: []const Zir.Inst.Index,
  37350     tag_type_ref: Zir.Inst.Ref,
  37351     any_values: bool,
  37352     fields_len: u32,
  37353     zir: Zir,
  37354     body_end: usize,
  37355 ) Zcu.CompileError!void {
  37356     const pt = sema.pt;
  37357     const zcu = pt.zcu;
  37358     const gpa = zcu.gpa;
  37359     const ip = &zcu.intern_pool;
  37360 
  37361     const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
  37362 
  37363     const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = .zero } };
  37364 
  37365     const int_tag_ty = ty: {
  37366         if (body.len != 0) {
  37367             _ = try sema.analyzeInlineBody(block, body, inst);
  37368         }
  37369 
  37370         if (tag_type_ref != .none) {
  37371             const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref);
  37372             if (ty.zigTypeTag(zcu) != .int and ty.zigTypeTag(zcu) != .comptime_int) {
  37373                 return sema.fail(block, tag_ty_src, "expected integer tag type, found '{f}'", .{ty.fmt(pt)});
  37374             }
  37375             break :ty ty;
  37376         } else if (fields_len == 0) {
  37377             break :ty try pt.intType(.unsigned, 0);
  37378         } else {
  37379             const bits = std.math.log2_int_ceil(usize, fields_len);
  37380             break :ty try pt.intType(.unsigned, bits);
  37381         }
  37382     };
  37383 
  37384     wip_ty.setTagTy(ip, int_tag_ty.toIntern());
  37385 
  37386     var extra_index = body_end + bit_bags_count;
  37387     var bit_bag_index: usize = body_end;
  37388     var cur_bit_bag: u32 = undefined;
  37389     var last_tag_val: ?Value = null;
  37390     for (0..fields_len) |field_i_usize| {
  37391         const field_i: u32 = @intCast(field_i_usize);
  37392         if (field_i % 32 == 0) {
  37393             cur_bit_bag = zir.extra[bit_bag_index];
  37394             bit_bag_index += 1;
  37395         }
  37396         const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0;
  37397         cur_bit_bag >>= 1;
  37398 
  37399         const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]);
  37400         const field_name_zir = zir.nullTerminatedString(field_name_index);
  37401         extra_index += 1; // field name
  37402 
  37403         const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
  37404 
  37405         const value_src: LazySrcLoc = .{
  37406             .base_node_inst = tracked_inst,
  37407             .offset = .{ .container_field_value = field_i },
  37408         };
  37409 
  37410         const tag_overflow = if (has_tag_value) overflow: {
  37411             const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  37412             extra_index += 1;
  37413             const tag_inst = try sema.resolveInst(tag_val_ref);
  37414             last_tag_val = try sema.resolveConstDefinedValue(block, .{
  37415                 .base_node_inst = tracked_inst,
  37416                 .offset = .{ .container_field_name = field_i },
  37417             }, tag_inst, .{ .simple = .enum_field_tag_value });
  37418             if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
  37419             last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
  37420             if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
  37421                 assert(conflict.kind == .value); // AstGen validated names are unique
  37422                 const other_field_src: LazySrcLoc = .{
  37423                     .base_node_inst = tracked_inst,
  37424                     .offset = .{ .container_field_value = conflict.prev_field_idx },
  37425                 };
  37426                 const msg = msg: {
  37427                     const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
  37428                     errdefer msg.destroy(gpa);
  37429                     try sema.errNote(other_field_src, msg, "other occurrence here", .{});
  37430                     break :msg msg;
  37431                 };
  37432                 return sema.failWithOwnedErrorMsg(block, msg);
  37433             }
  37434             break :overflow false;
  37435         } else if (any_values) overflow: {
  37436             if (last_tag_val) |last_tag| {
  37437                 const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag);
  37438                 last_tag_val = result.val;
  37439                 if (result.overflow) break :overflow true;
  37440             } else {
  37441                 last_tag_val = try pt.intValue(int_tag_ty, 0);
  37442             }
  37443             if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
  37444                 assert(conflict.kind == .value); // AstGen validated names are unique
  37445                 const other_field_src: LazySrcLoc = .{
  37446                     .base_node_inst = tracked_inst,
  37447                     .offset = .{ .container_field_value = conflict.prev_field_idx },
  37448                 };
  37449                 const msg = msg: {
  37450                     const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
  37451                     errdefer msg.destroy(gpa);
  37452                     try sema.errNote(other_field_src, msg, "other occurrence here", .{});
  37453                     break :msg msg;
  37454                 };
  37455                 return sema.failWithOwnedErrorMsg(block, msg);
  37456             }
  37457             break :overflow false;
  37458         } else overflow: {
  37459             assert(wip_ty.nextField(ip, field_name, .none) == null);
  37460             last_tag_val = try pt.intValue(.comptime_int, field_i);
  37461             if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
  37462             last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
  37463             break :overflow false;
  37464         };
  37465 
  37466         if (tag_overflow) {
  37467             const msg = try sema.errMsg(value_src, "enumeration value '{f}' too large for type '{f}'", .{
  37468                 last_tag_val.?.fmtValueSema(pt, sema), int_tag_ty.fmt(pt),
  37469             });
  37470             return sema.failWithOwnedErrorMsg(block, msg);
  37471         }
  37472     }
  37473     if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
  37474         if (fields_len >= 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) {
  37475             return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
  37476         }
  37477     }
  37478 }
  37479 
  37480 pub const bitCastVal = @import("Sema/bitcast.zig").bitCast;
  37481 pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice;
  37482 
  37483 const loadComptimePtr = @import("Sema/comptime_ptr_access.zig").loadComptimePtr;
  37484 const ComptimeLoadResult = @import("Sema/comptime_ptr_access.zig").ComptimeLoadResult;
  37485 const storeComptimePtr = @import("Sema/comptime_ptr_access.zig").storeComptimePtr;
  37486 const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStoreResult;
  37487 
  37488 pub fn getBuiltinType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!Type {
  37489     assert(decl.kind() == .type);
  37490     try sema.ensureMemoizedStateResolved(src, decl.stage());
  37491     return .fromInterned(sema.pt.zcu.builtin_decl_values.get(decl));
  37492 }
  37493 pub fn getBuiltin(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!InternPool.Index {
  37494     assert(decl.kind() != .type);
  37495     try sema.ensureMemoizedStateResolved(src, decl.stage());
  37496     return sema.pt.zcu.builtin_decl_values.get(decl);
  37497 }
  37498 
  37499 pub const NavPtrModifiers = struct {
  37500     alignment: Alignment,
  37501     @"linksection": InternPool.OptionalNullTerminatedString,
  37502     @"addrspace": std.builtin.AddressSpace,
  37503 };
  37504 
  37505 pub fn resolveNavPtrModifiers(
  37506     sema: *Sema,
  37507     block: *Block,
  37508     zir_decl: Zir.Inst.Declaration.Unwrapped,
  37509     decl_inst: Zir.Inst.Index,
  37510     nav_ty: Type,
  37511 ) CompileError!NavPtrModifiers {
  37512     const pt = sema.pt;
  37513     const zcu = pt.zcu;
  37514     const gpa = zcu.gpa;
  37515     const ip = &zcu.intern_pool;
  37516 
  37517     const align_src = block.src(.{ .node_offset_var_decl_align = .zero });
  37518     const section_src = block.src(.{ .node_offset_var_decl_section = .zero });
  37519     const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = .zero });
  37520 
  37521     const alignment: InternPool.Alignment = a: {
  37522         const align_body = zir_decl.align_body orelse break :a .none;
  37523         const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst);
  37524         break :a try sema.analyzeAsAlign(block, align_src, align_ref);
  37525     };
  37526 
  37527     const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
  37528         const linksection_body = zir_decl.linksection_body orelse break :ls .none;
  37529         const linksection_ref = try sema.resolveInlineBody(block, linksection_body, decl_inst);
  37530         const bytes = try sema.toConstString(block, section_src, linksection_ref, .{ .simple = .@"linksection" });
  37531         if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
  37532             return sema.fail(block, section_src, "linksection cannot contain null bytes", .{});
  37533         } else if (bytes.len == 0) {
  37534             return sema.fail(block, section_src, "linksection cannot be empty", .{});
  37535         }
  37536         break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
  37537     };
  37538 
  37539     const @"addrspace": std.builtin.AddressSpace = as: {
  37540         const addrspace_ctx: std.builtin.AddressSpace.Context = switch (zir_decl.kind) {
  37541             .@"var" => .variable,
  37542             else => switch (nav_ty.zigTypeTag(zcu)) {
  37543                 .@"fn" => .function,
  37544                 else => .constant,
  37545             },
  37546         };
  37547         const target = zcu.getTarget();
  37548         const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {
  37549             .function => target_util.defaultAddressSpace(target, .function),
  37550             .variable => target_util.defaultAddressSpace(target, .global_mutable),
  37551             .constant => target_util.defaultAddressSpace(target, .global_constant),
  37552             else => unreachable,
  37553         };
  37554         const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst);
  37555         break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx);
  37556     };
  37557 
  37558     return .{
  37559         .alignment = alignment,
  37560         .@"linksection" = @"linksection",
  37561         .@"addrspace" = @"addrspace",
  37562     };
  37563 }
  37564 
  37565 pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, builtin_namespace: InternPool.NamespaceIndex, stage: InternPool.MemoizedStateStage) CompileError!bool {
  37566     const pt = sema.pt;
  37567     const zcu = pt.zcu;
  37568     const ip = &zcu.intern_pool;
  37569     const gpa = zcu.gpa;
  37570 
  37571     var any_changed = false;
  37572 
  37573     inline for (comptime std.enums.values(Zcu.BuiltinDecl)) |builtin_decl| {
  37574         if (stage == comptime builtin_decl.stage()) {
  37575             const parent_ns: Zcu.Namespace.Index, const parent_name: []const u8, const name: []const u8 = switch (comptime builtin_decl.access()) {
  37576                 .direct => |name| .{ builtin_namespace, "std.builtin", name },
  37577                 .nested => |nested| access: {
  37578                     const parent_ty: Type = .fromInterned(zcu.builtin_decl_values.get(nested[0]));
  37579                     const parent_ns = parent_ty.getNamespace(zcu).unwrap() orelse {
  37580                         return sema.fail(block, simple_src, "std.builtin.{s} is not a container type", .{@tagName(nested[0])});
  37581                     };
  37582                     break :access .{ parent_ns, "std.builtin." ++ @tagName(nested[0]), nested[1] };
  37583                 },
  37584             };
  37585 
  37586             const name_nts = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
  37587             const nav = try sema.namespaceLookup(block, simple_src, parent_ns, name_nts) orelse
  37588                 return sema.fail(block, simple_src, "{s} missing {s}", .{ parent_name, name });
  37589 
  37590             const src: LazySrcLoc = .{
  37591                 .base_node_inst = ip.getNav(nav).srcInst(ip),
  37592                 .offset = .nodeOffset(.zero),
  37593             };
  37594 
  37595             const result = try sema.analyzeNavVal(block, src, nav);
  37596 
  37597             const uncoerced_val = try sema.resolveConstDefinedValue(block, src, result, null);
  37598             const maybe_lazy_val: Value = switch (builtin_decl.kind()) {
  37599                 .type => if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .type) {
  37600                     return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name });
  37601                 } else val: {
  37602                     try uncoerced_val.toType().resolveFully(pt);
  37603                     break :val uncoerced_val;
  37604                 },
  37605                 .func => val: {
  37606                     const func_ty = try sema.getExpectedBuiltinFnType(builtin_decl);
  37607                     const coerced = try sema.coerce(block, func_ty, Air.internedToRef(uncoerced_val.toIntern()), src);
  37608                     break :val .fromInterned(coerced.toInterned().?);
  37609                 },
  37610                 .string => val: {
  37611                     const coerced = try sema.coerce(block, .slice_const_u8, Air.internedToRef(uncoerced_val.toIntern()), src);
  37612                     break :val .fromInterned(coerced.toInterned().?);
  37613                 },
  37614             };
  37615             const val = try sema.resolveLazyValue(maybe_lazy_val);
  37616 
  37617             const prev = zcu.builtin_decl_values.get(builtin_decl);
  37618             if (val.toIntern() != prev) {
  37619                 zcu.builtin_decl_values.set(builtin_decl, val.toIntern());
  37620                 any_changed = true;
  37621             }
  37622         }
  37623     }
  37624 
  37625     return any_changed;
  37626 }
  37627 
  37628 /// Given that `decl.kind() == .func`, get the type expected of the function.
  37629 fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Type {
  37630     const pt = sema.pt;
  37631     return switch (decl) {
  37632         // `noinline fn () void`
  37633         .returnError => try pt.funcType(.{
  37634             .param_types = &.{},
  37635             .return_type = .void_type,
  37636             .is_noinline = true,
  37637         }),
  37638 
  37639         // `fn ([]const u8, ?usize) noreturn`
  37640         .@"panic.call" => try pt.funcType(.{
  37641             .param_types = &.{
  37642                 .slice_const_u8_type,
  37643                 (try pt.optionalType(.usize_type)).toIntern(),
  37644             },
  37645             .return_type = .noreturn_type,
  37646         }),
  37647 
  37648         // `fn (anytype, anytype) noreturn`
  37649         .@"panic.sentinelMismatch",
  37650         .@"panic.inactiveUnionField",
  37651         => try pt.funcType(.{
  37652             .param_types = &.{ .generic_poison_type, .generic_poison_type },
  37653             .return_type = .noreturn_type,
  37654             .is_generic = true,
  37655         }),
  37656 
  37657         // `fn (anyerror) noreturn`
  37658         .@"panic.unwrapError" => try pt.funcType(.{
  37659             .param_types = &.{.anyerror_type},
  37660             .return_type = .noreturn_type,
  37661         }),
  37662 
  37663         // `fn (usize) noreturn`
  37664         .@"panic.sliceCastLenRemainder" => try pt.funcType(.{
  37665             .param_types = &.{.usize_type},
  37666             .return_type = .noreturn_type,
  37667         }),
  37668 
  37669         // `fn (usize, usize) noreturn`
  37670         .@"panic.outOfBounds",
  37671         .@"panic.startGreaterThanEnd",
  37672         => try pt.funcType(.{
  37673             .param_types = &.{ .usize_type, .usize_type },
  37674             .return_type = .noreturn_type,
  37675         }),
  37676 
  37677         // `fn () noreturn`
  37678         .@"panic.reachedUnreachable",
  37679         .@"panic.unwrapNull",
  37680         .@"panic.castToNull",
  37681         .@"panic.incorrectAlignment",
  37682         .@"panic.invalidErrorCode",
  37683         .@"panic.integerOutOfBounds",
  37684         .@"panic.integerOverflow",
  37685         .@"panic.shlOverflow",
  37686         .@"panic.shrOverflow",
  37687         .@"panic.divideByZero",
  37688         .@"panic.exactDivisionRemainder",
  37689         .@"panic.integerPartOutOfBounds",
  37690         .@"panic.corruptSwitch",
  37691         .@"panic.shiftRhsTooBig",
  37692         .@"panic.invalidEnumValue",
  37693         .@"panic.forLenMismatch",
  37694         .@"panic.copyLenMismatch",
  37695         .@"panic.memcpyAlias",
  37696         .@"panic.noreturnReturned",
  37697         => try pt.funcType(.{
  37698             .param_types = &.{},
  37699             .return_type = .noreturn_type,
  37700         }),
  37701 
  37702         else => unreachable,
  37703     };
  37704 }