zig

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

blob 8ea0a19f (1596766B) - 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.ArrayList(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.ArrayList(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_val_node                => try sema.zirElemValNode(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             .vec_arr_elem_type            => try sema.zirVecArrElemType(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_val                    => try sema.zirFieldVal(block, inst),
   1215             .field_val_named              => try sema.zirFieldValNamed(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                 };
   1472             },
   1473 
   1474             // Instructions that we know can *never* be noreturn based solely on
   1475             // their tag. We avoid needlessly checking if they are noreturn and
   1476             // continue the loop.
   1477             // We also know that they cannot be referenced later, so we avoid
   1478             // putting them into the map.
   1479             .dbg_stmt => {
   1480                 try sema.zirDbgStmt(block, inst);
   1481                 i += 1;
   1482                 continue;
   1483             },
   1484             .dbg_var_ptr => {
   1485                 try sema.zirDbgVar(block, inst, .dbg_var_ptr);
   1486                 i += 1;
   1487                 continue;
   1488             },
   1489             .dbg_var_val => {
   1490                 try sema.zirDbgVar(block, inst, .dbg_var_val);
   1491                 i += 1;
   1492                 continue;
   1493             },
   1494             .ensure_err_union_payload_void => {
   1495                 try sema.zirEnsureErrUnionPayloadVoid(block, inst);
   1496                 i += 1;
   1497                 continue;
   1498             },
   1499             .ensure_result_non_error => {
   1500                 try sema.zirEnsureResultNonError(block, inst);
   1501                 i += 1;
   1502                 continue;
   1503             },
   1504             .ensure_result_used => {
   1505                 try sema.zirEnsureResultUsed(block, inst);
   1506                 i += 1;
   1507                 continue;
   1508             },
   1509             .set_eval_branch_quota => {
   1510                 try sema.zirSetEvalBranchQuota(block, inst);
   1511                 i += 1;
   1512                 continue;
   1513             },
   1514             .atomic_store => {
   1515                 try sema.zirAtomicStore(block, inst);
   1516                 i += 1;
   1517                 continue;
   1518             },
   1519             .store_node => {
   1520                 try sema.zirStoreNode(block, inst);
   1521                 i += 1;
   1522                 continue;
   1523             },
   1524             .store_to_inferred_ptr => {
   1525                 try sema.zirStoreToInferredPtr(block, inst);
   1526                 i += 1;
   1527                 continue;
   1528             },
   1529             .validate_struct_init_ty => {
   1530                 try sema.zirValidateStructInitTy(block, inst, false);
   1531                 i += 1;
   1532                 continue;
   1533             },
   1534             .validate_struct_init_result_ty => {
   1535                 try sema.zirValidateStructInitTy(block, inst, true);
   1536                 i += 1;
   1537                 continue;
   1538             },
   1539             .validate_array_init_ty => {
   1540                 try sema.zirValidateArrayInitTy(block, inst, false);
   1541                 i += 1;
   1542                 continue;
   1543             },
   1544             .validate_array_init_result_ty => {
   1545                 try sema.zirValidateArrayInitTy(block, inst, true);
   1546                 i += 1;
   1547                 continue;
   1548             },
   1549             .validate_ptr_struct_init => {
   1550                 try sema.zirValidatePtrStructInit(block, inst);
   1551                 i += 1;
   1552                 continue;
   1553             },
   1554             .validate_ptr_array_init => {
   1555                 try sema.zirValidatePtrArrayInit(block, inst);
   1556                 i += 1;
   1557                 continue;
   1558             },
   1559             .validate_deref => {
   1560                 try sema.zirValidateDeref(block, inst);
   1561                 i += 1;
   1562                 continue;
   1563             },
   1564             .validate_destructure => {
   1565                 try sema.zirValidateDestructure(block, inst);
   1566                 i += 1;
   1567                 continue;
   1568             },
   1569             .validate_ref_ty => {
   1570                 try sema.zirValidateRefTy(block, inst);
   1571                 i += 1;
   1572                 continue;
   1573             },
   1574             .validate_const => {
   1575                 try sema.zirValidateConst(block, inst);
   1576                 i += 1;
   1577                 continue;
   1578             },
   1579             .@"export" => {
   1580                 try sema.zirExport(block, inst);
   1581                 i += 1;
   1582                 continue;
   1583             },
   1584             .set_runtime_safety => {
   1585                 try sema.zirSetRuntimeSafety(block, inst);
   1586                 i += 1;
   1587                 continue;
   1588             },
   1589             .param => {
   1590                 try sema.zirParam(block, inst, false);
   1591                 i += 1;
   1592                 continue;
   1593             },
   1594             .param_comptime => {
   1595                 try sema.zirParam(block, inst, true);
   1596                 i += 1;
   1597                 continue;
   1598             },
   1599             .param_anytype => {
   1600                 try sema.zirParamAnytype(block, inst, false);
   1601                 i += 1;
   1602                 continue;
   1603             },
   1604             .param_anytype_comptime => {
   1605                 try sema.zirParamAnytype(block, inst, true);
   1606                 i += 1;
   1607                 continue;
   1608             },
   1609             .memcpy => {
   1610                 try sema.zirMemcpy(block, inst, .memcpy, true);
   1611                 i += 1;
   1612                 continue;
   1613             },
   1614             .memmove => {
   1615                 try sema.zirMemcpy(block, inst, .memmove, false);
   1616                 i += 1;
   1617                 continue;
   1618             },
   1619             .memset => {
   1620                 try sema.zirMemset(block, inst);
   1621                 i += 1;
   1622                 continue;
   1623             },
   1624             .check_comptime_control_flow => {
   1625                 if (!block.isComptime()) {
   1626                     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   1627                     const src = block.nodeOffset(inst_data.src_node);
   1628                     const inline_block = inst_data.operand.toIndex().?;
   1629 
   1630                     var check_block = block;
   1631                     const target_runtime_index = while (true) {
   1632                         if (check_block.inline_block == inline_block.toOptional()) {
   1633                             break check_block.runtime_index;
   1634                         }
   1635                         check_block = check_block.parent.?;
   1636                     };
   1637 
   1638                     if (@intFromEnum(target_runtime_index) < @intFromEnum(block.runtime_index)) {
   1639                         const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
   1640                         const msg = msg: {
   1641                             const msg = try sema.errMsg(src, "comptime control flow inside runtime block", .{});
   1642                             errdefer msg.destroy(sema.gpa);
   1643                             try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
   1644                             break :msg msg;
   1645                         };
   1646                         return sema.failWithOwnedErrorMsg(block, msg);
   1647                     }
   1648                 }
   1649                 i += 1;
   1650                 continue;
   1651             },
   1652             .save_err_ret_index => {
   1653                 try sema.zirSaveErrRetIndex(block, inst);
   1654                 i += 1;
   1655                 continue;
   1656             },
   1657             .restore_err_ret_index_unconditional => {
   1658                 const un_node = datas[@intFromEnum(inst)].un_node;
   1659                 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), un_node.operand, .none);
   1660                 i += 1;
   1661                 continue;
   1662             },
   1663             .restore_err_ret_index_fn_entry => {
   1664                 const un_node = datas[@intFromEnum(inst)].un_node;
   1665                 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), .none, un_node.operand);
   1666                 i += 1;
   1667                 continue;
   1668             },
   1669 
   1670             // Special case instructions to handle comptime control flow.
   1671             .@"break" => {
   1672                 if (block.isComptime()) {
   1673                     sema.comptime_break_inst = inst;
   1674                     return error.ComptimeBreak;
   1675                 } else {
   1676                     try sema.zirBreak(block, inst);
   1677                     break;
   1678                 }
   1679             },
   1680             .break_inline => {
   1681                 sema.comptime_break_inst = inst;
   1682                 return error.ComptimeBreak;
   1683             },
   1684             .repeat => {
   1685                 if (block.isComptime()) {
   1686                     // Send comptime control flow back to the beginning of this block.
   1687                     const src = block.nodeOffset(datas[@intFromEnum(inst)].node);
   1688                     try sema.emitBackwardBranch(block, src);
   1689                     i = 0;
   1690                     continue;
   1691                 } else {
   1692                     // We are definitely called by `zirLoop`, which will treat the
   1693                     // fact that this body does not terminate `noreturn` as an
   1694                     // implicit repeat.
   1695                     // TODO: since AIR has `repeat` now, we could change ZIR to generate
   1696                     // more optimal code utilizing `repeat` instructions across blocks!
   1697                     break;
   1698                 }
   1699             },
   1700             .repeat_inline => {
   1701                 // Send comptime control flow back to the beginning of this block.
   1702                 const src = block.nodeOffset(datas[@intFromEnum(inst)].node);
   1703                 try sema.emitBackwardBranch(block, src);
   1704                 i = 0;
   1705                 continue;
   1706             },
   1707             .switch_continue => if (block.isComptime()) {
   1708                 sema.comptime_break_inst = inst;
   1709                 return error.ComptimeBreak;
   1710             } else {
   1711                 try sema.zirSwitchContinue(block, inst);
   1712                 break;
   1713             },
   1714 
   1715             .loop => if (block.isComptime()) {
   1716                 continue :inst .block_inline;
   1717             } else try sema.zirLoop(block, inst),
   1718 
   1719             .block => if (block.isComptime()) {
   1720                 continue :inst .block_inline;
   1721             } else try sema.zirBlock(block, inst),
   1722 
   1723             .block_comptime => {
   1724                 const pl_node = datas[@intFromEnum(inst)].pl_node;
   1725                 const src = block.nodeOffset(pl_node.src_node);
   1726                 const extra = sema.code.extraData(Zir.Inst.BlockComptime, pl_node.payload_index);
   1727                 const block_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1728 
   1729                 var child_block = block.makeSubBlock();
   1730                 defer child_block.instructions.deinit(sema.gpa);
   1731 
   1732                 // We won't have any merges, but we must ensure this block is properly labeled for
   1733                 // any `.restore_err_ret_index_*` instructions.
   1734                 var label: Block.Label = .{
   1735                     .zir_block = inst,
   1736                     .merges = undefined,
   1737                 };
   1738                 child_block.label = &label;
   1739 
   1740                 child_block.comptime_reason = .{ .reason = .{
   1741                     .src = src,
   1742                     .r = .{ .simple = extra.data.reason },
   1743                 } };
   1744 
   1745                 const result = try sema.resolveInlineBody(&child_block, block_body, inst);
   1746 
   1747                 // Only check for the result being comptime-known in the outermost `block_comptime`.
   1748                 // That way, AstGen can safely elide redundant `block_comptime` without affecting semantics.
   1749                 if (!block.isComptime() and !try sema.isComptimeKnown(result)) {
   1750                     return sema.failWithNeededComptime(&child_block, src, null);
   1751                 }
   1752 
   1753                 break :inst result;
   1754             },
   1755 
   1756             .block_inline => blk: {
   1757                 // Directly analyze the block body without introducing a new block.
   1758                 // However, in the case of a corresponding break_inline which reaches
   1759                 // through a runtime conditional branch, we must retroactively emit
   1760                 // a block, so we remember the block index here just in case.
   1761                 const block_index = block.instructions.items.len;
   1762                 const inst_data = datas[@intFromEnum(inst)].pl_node;
   1763                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   1764                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1765                 const gpa = sema.gpa;
   1766 
   1767                 const BreakResult = struct {
   1768                     block_inst: Zir.Inst.Index,
   1769                     operand: Zir.Inst.Ref,
   1770                 };
   1771 
   1772                 const opt_break_data: ?BreakResult, const need_debug_scope = b: {
   1773                     // Create a temporary child block so that this inline block is properly
   1774                     // labeled for any .restore_err_ret_index instructions
   1775                     var child_block = block.makeSubBlock();
   1776                     var need_debug_scope = false;
   1777                     child_block.need_debug_scope = &need_debug_scope;
   1778 
   1779                     // If this block contains a function prototype, we need to reset the
   1780                     // current list of parameters and restore it later.
   1781                     // Note: this probably needs to be resolved in a more general manner.
   1782                     const tag_index = @intFromEnum(inline_body[inline_body.len - 1]);
   1783                     child_block.inline_block = (if (tags[tag_index] == .repeat_inline)
   1784                         inline_body[0]
   1785                     else
   1786                         inst).toOptional();
   1787 
   1788                     var label: Block.Label = .{
   1789                         .zir_block = inst,
   1790                         .merges = undefined,
   1791                     };
   1792                     child_block.label = &label;
   1793 
   1794                     // Write these instructions directly into the parent block
   1795                     child_block.instructions = block.instructions;
   1796                     defer block.instructions = child_block.instructions;
   1797 
   1798                     const break_result: ?BreakResult = if (sema.analyzeBodyInner(&child_block, inline_body)) |_| r: {
   1799                         break :r null;
   1800                     } else |err| switch (err) {
   1801                         error.ComptimeBreak => brk_res: {
   1802                             const break_inst = sema.comptime_break_inst;
   1803                             const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
   1804                             const break_extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
   1805                             break :brk_res .{
   1806                                 .block_inst = break_extra.block_inst,
   1807                                 .operand = break_data.operand,
   1808                             };
   1809                         },
   1810                         else => |e| return e,
   1811                     };
   1812 
   1813                     if (need_debug_scope) {
   1814                         _ = try sema.ensurePostHoc(block, inst);
   1815                     }
   1816 
   1817                     break :b .{ break_result, need_debug_scope };
   1818                 };
   1819 
   1820                 // A runtime conditional branch that needs a post-hoc block to be
   1821                 // emitted communicates this by mapping the block index into the inst map.
   1822                 if (map.get(inst)) |new_block_ref| ph: {
   1823                     // Comptime control flow populates the map, so we don't actually know
   1824                     // if this is a post-hoc runtime block until we check the
   1825                     // post_hoc_block map.
   1826                     const new_block_inst = new_block_ref.toIndex() orelse break :ph;
   1827                     const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse
   1828                         break :ph;
   1829 
   1830                     // In this case we need to move all the instructions starting at
   1831                     // block_index from the current block into this new one.
   1832 
   1833                     if (opt_break_data) |break_data| {
   1834                         // This is a comptime break which we now change to a runtime break
   1835                         // since it crosses a runtime branch.
   1836                         // It may pass through our currently being analyzed block_inline or it
   1837                         // may point directly to it. In the latter case, this modifies the
   1838                         // block that we looked up in the post_hoc_blocks map above.
   1839                         try sema.addRuntimeBreak(block, break_data.block_inst, break_data.operand);
   1840                     }
   1841 
   1842                     try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]);
   1843                     block.instructions.items.len = block_index;
   1844 
   1845                     const block_result = try sema.resolveAnalyzedBlock(block, block.nodeOffset(inst_data.src_node), &labeled_block.block, &labeled_block.label.merges, need_debug_scope);
   1846                     {
   1847                         // Destroy the ad-hoc block entry so that it does not interfere with
   1848                         // the next iteration of comptime control flow, if any.
   1849                         labeled_block.destroy(gpa);
   1850                         assert(sema.post_hoc_blocks.remove(new_block_inst));
   1851                     }
   1852 
   1853                     break :blk block_result;
   1854                 }
   1855 
   1856                 const break_data = opt_break_data orelse break;
   1857                 if (inst == break_data.block_inst) {
   1858                     break :blk try sema.resolveInst(break_data.operand);
   1859                 } else {
   1860                     // `comptime_break_inst` preserved from `analyzeBodyInner` above.
   1861                     return error.ComptimeBreak;
   1862                 }
   1863             },
   1864             .condbr => if (block.isComptime()) {
   1865                 continue :inst .condbr_inline;
   1866             } else {
   1867                 try sema.zirCondbr(block, inst);
   1868                 break;
   1869             },
   1870             .condbr_inline => {
   1871                 const inst_data = datas[@intFromEnum(inst)].pl_node;
   1872                 const cond_src = block.src(.{ .node_offset_if_cond = inst_data.src_node });
   1873                 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
   1874                 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
   1875                 const else_body = sema.code.bodySlice(
   1876                     extra.end + then_body.len,
   1877                     extra.data.else_body_len,
   1878                 );
   1879                 const uncasted_cond = try sema.resolveInst(extra.data.condition);
   1880                 const cond = try sema.coerce(block, .bool, uncasted_cond, cond_src);
   1881                 const cond_val = try sema.resolveConstDefinedValue(
   1882                     block,
   1883                     cond_src,
   1884                     cond,
   1885                     // If this block is comptime, it's more helpful to just give the outer message.
   1886                     // This is particularly true if this came from a comptime `condbr` above.
   1887                     if (block.isComptime()) null else .{ .simple = .inline_loop_operand },
   1888                 );
   1889                 const inline_body = if (cond_val.toBool()) then_body else else_body;
   1890 
   1891                 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
   1892                 const old_runtime_index = block.runtime_index;
   1893                 defer block.runtime_index = old_runtime_index;
   1894 
   1895                 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
   1896                 break :inst result;
   1897             },
   1898             .@"try" => blk: {
   1899                 if (!block.isComptime()) break :blk try sema.zirTry(block, inst);
   1900                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   1901                 const src = block.nodeOffset(inst_data.src_node);
   1902                 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node });
   1903                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
   1904                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1905                 const err_union = try sema.resolveInst(extra.data.operand);
   1906                 const err_union_ty = sema.typeOf(err_union);
   1907                 if (err_union_ty.zigTypeTag(zcu) != .error_union) {
   1908                     return sema.failWithOwnedErrorMsg(block, msg: {
   1909                         const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
   1910                         errdefer msg.destroy(sema.gpa);
   1911                         try sema.addDeclaredHereNote(msg, err_union_ty);
   1912                         try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
   1913                         break :msg msg;
   1914                     });
   1915                 }
   1916                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
   1917                 assert(is_non_err != .none);
   1918                 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
   1919                 if (is_non_err_val.toBool()) {
   1920                     break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
   1921                 }
   1922                 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
   1923                 break :blk result;
   1924             },
   1925             .try_ptr => blk: {
   1926                 if (!block.isComptime()) break :blk try sema.zirTryPtr(block, inst);
   1927                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   1928                 const src = block.nodeOffset(inst_data.src_node);
   1929                 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node });
   1930                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
   1931                 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
   1932                 const operand = try sema.resolveInst(extra.data.operand);
   1933                 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
   1934                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
   1935                 assert(is_non_err != .none);
   1936                 const is_non_err_val = try sema.resolveConstDefinedValue(block, operand_src, is_non_err, null);
   1937                 if (is_non_err_val.toBool()) {
   1938                     break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
   1939                 }
   1940                 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
   1941                 break :blk result;
   1942             },
   1943             .@"defer" => blk: {
   1944                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"defer";
   1945                 const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len);
   1946                 if (sema.analyzeBodyInner(block, defer_body)) |_| {
   1947                     // The defer terminated noreturn - no more analysis needed.
   1948                     break;
   1949                 } else |err| switch (err) {
   1950                     error.ComptimeBreak => {},
   1951                     else => |e| return e,
   1952                 }
   1953                 if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) {
   1954                     return error.ComptimeBreak;
   1955                 }
   1956                 break :blk .void_value;
   1957             },
   1958             .defer_err_code => blk: {
   1959                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].defer_err_code;
   1960                 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
   1961                 const defer_body = sema.code.bodySlice(extra.index, extra.len);
   1962                 const err_code = try sema.resolveInst(inst_data.err_code);
   1963                 try map.ensureSpaceForInstructions(sema.gpa, defer_body);
   1964                 map.putAssumeCapacity(extra.remapped_err_code, err_code);
   1965                 if (sema.analyzeBodyInner(block, defer_body)) |_| {
   1966                     // The defer terminated noreturn - no more analysis needed.
   1967                     break;
   1968                 } else |err| switch (err) {
   1969                     error.ComptimeBreak => {},
   1970                     else => |e| return e,
   1971                 }
   1972                 if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) {
   1973                     return error.ComptimeBreak;
   1974                 }
   1975                 break :blk .void_value;
   1976             },
   1977         };
   1978         if (sema.isNoReturn(air_inst)) {
   1979             // We're going to assume that the body itself is noreturn, so let's ensure that now
   1980             assert(block.instructions.items.len > 0);
   1981             assert(sema.isNoReturn(block.instructions.items[block.instructions.items.len - 1].toRef()));
   1982             break;
   1983         }
   1984         map.putAssumeCapacity(inst, air_inst);
   1985         i += 1;
   1986     }
   1987 }
   1988 
   1989 pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
   1990     if (zir_ref == .none) {
   1991         return .none;
   1992     } else {
   1993         return resolveInst(sema, zir_ref);
   1994     }
   1995 }
   1996 
   1997 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
   1998     assert(zir_ref != .none);
   1999     if (zir_ref.toIndex()) |i| {
   2000         return sema.inst_map.get(i).?;
   2001     }
   2002     // First section of indexes correspond to a set number of constant values.
   2003     // We intentionally map the same indexes to the same values between ZIR and AIR.
   2004     return @enumFromInt(@intFromEnum(zir_ref));
   2005 }
   2006 
   2007 fn resolveConstBool(
   2008     sema: *Sema,
   2009     block: *Block,
   2010     src: LazySrcLoc,
   2011     zir_ref: Zir.Inst.Ref,
   2012     reason: ComptimeReason,
   2013 ) !bool {
   2014     const air_inst = try sema.resolveInst(zir_ref);
   2015     const wanted_type: Type = .bool;
   2016     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   2017     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
   2018     return val.toBool();
   2019 }
   2020 
   2021 fn resolveConstString(
   2022     sema: *Sema,
   2023     block: *Block,
   2024     src: LazySrcLoc,
   2025     zir_ref: Zir.Inst.Ref,
   2026     reason: ComptimeReason,
   2027 ) ![]u8 {
   2028     const air_inst = try sema.resolveInst(zir_ref);
   2029     return sema.toConstString(block, src, air_inst, reason);
   2030 }
   2031 
   2032 pub fn toConstString(
   2033     sema: *Sema,
   2034     block: *Block,
   2035     src: LazySrcLoc,
   2036     air_inst: Air.Inst.Ref,
   2037     reason: ComptimeReason,
   2038 ) ![]u8 {
   2039     const pt = sema.pt;
   2040     const coerced_inst = try sema.coerce(block, .slice_const_u8, air_inst, src);
   2041     const slice_val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
   2042     const arr_val = try sema.derefSliceAsArray(block, src, slice_val, reason);
   2043     return arr_val.toAllocatedBytes(arr_val.typeOf(pt.zcu), sema.arena, pt);
   2044 }
   2045 
   2046 pub fn resolveConstStringIntern(
   2047     sema: *Sema,
   2048     block: *Block,
   2049     src: LazySrcLoc,
   2050     zir_ref: Zir.Inst.Ref,
   2051     reason: ComptimeReason,
   2052 ) !InternPool.NullTerminatedString {
   2053     const air_inst = try sema.resolveInst(zir_ref);
   2054     const wanted_type: Type = .slice_const_u8;
   2055     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   2056     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
   2057     return sema.sliceToIpString(block, src, val, reason);
   2058 }
   2059 
   2060 fn resolveTypeOrPoison(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !?Type {
   2061     const air_inst = try sema.resolveInst(zir_ref);
   2062     const ty = try sema.analyzeAsType(block, src, air_inst);
   2063     if (ty.isGenericPoison()) return null;
   2064     return ty;
   2065 }
   2066 
   2067 pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
   2068     return (try sema.resolveTypeOrPoison(block, src, zir_ref)).?;
   2069 }
   2070 
   2071 fn resolveDestType(
   2072     sema: *Sema,
   2073     block: *Block,
   2074     src: LazySrcLoc,
   2075     zir_ref: Zir.Inst.Ref,
   2076     strat: enum { remove_eu_opt, remove_eu, remove_opt },
   2077     builtin_name: []const u8,
   2078 ) !Type {
   2079     const pt = sema.pt;
   2080     const zcu = pt.zcu;
   2081     const remove_eu = switch (strat) {
   2082         .remove_eu_opt, .remove_eu => true,
   2083         .remove_opt => false,
   2084     };
   2085     const remove_opt = switch (strat) {
   2086         .remove_eu_opt, .remove_opt => true,
   2087         .remove_eu => false,
   2088     };
   2089 
   2090     const raw_ty = try sema.resolveTypeOrPoison(block, src, zir_ref) orelse {
   2091         // Cast builtins use their result type as the destination type, but
   2092         // it could be an anytype argument, which we can't catch in AstGen.
   2093         const msg = msg: {
   2094             const msg = try sema.errMsg(src, "{s} must have a known result type", .{builtin_name});
   2095             errdefer msg.destroy(sema.gpa);
   2096             switch (sema.genericPoisonReason(block, zir_ref)) {
   2097                 .anytype_param => |call_src| try sema.errNote(call_src, msg, "result type is unknown due to anytype parameter", .{}),
   2098                 .anyopaque_ptr => |ptr_src| try sema.errNote(ptr_src, msg, "result type is unknown due to opaque pointer type", .{}),
   2099                 .unknown => {},
   2100             }
   2101             try sema.errNote(src, msg, "use @as to provide explicit result type", .{});
   2102             break :msg msg;
   2103         };
   2104         return sema.failWithOwnedErrorMsg(block, msg);
   2105     };
   2106 
   2107     if (remove_eu and raw_ty.zigTypeTag(zcu) == .error_union) {
   2108         const eu_child = raw_ty.errorUnionPayload(zcu);
   2109         if (remove_opt and eu_child.zigTypeTag(zcu) == .optional) {
   2110             return eu_child.childType(zcu);
   2111         }
   2112         return eu_child;
   2113     }
   2114     if (remove_opt and raw_ty.zigTypeTag(zcu) == .optional) {
   2115         return raw_ty.childType(zcu);
   2116     }
   2117     return raw_ty;
   2118 }
   2119 
   2120 const GenericPoisonReason = union(enum) {
   2121     anytype_param: LazySrcLoc,
   2122     anyopaque_ptr: LazySrcLoc,
   2123     unknown,
   2124 };
   2125 
   2126 /// Backtracks through ZIR instructions to determine the reason a generic poison
   2127 /// type was created. Used for error reporting.
   2128 fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoisonReason {
   2129     var cur = ref;
   2130     while (true) {
   2131         const inst = cur.toIndex() orelse return .unknown;
   2132         switch (sema.code.instructions.items(.tag)[@intFromEnum(inst)]) {
   2133             .validate_array_init_ref_ty => {
   2134                 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   2135                 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
   2136                 cur = extra.ptr_ty;
   2137             },
   2138             .array_init_elem_type => {
   2139                 const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
   2140                 cur = bin.lhs;
   2141             },
   2142             .indexable_ptr_elem_type, .vec_arr_elem_type => {
   2143                 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   2144                 cur = un_node.operand;
   2145             },
   2146             .struct_init_field_type => {
   2147                 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   2148                 const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data;
   2149                 cur = extra.container_type;
   2150             },
   2151             .elem_type => {
   2152                 // There are two cases here: the pointer type may already have been
   2153                 // generic poison, or it may have been an anyopaque pointer.
   2154                 const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   2155                 const operand_ref = try sema.resolveInst(un_node.operand);
   2156                 const operand_val = operand_ref.toInterned() orelse return .unknown;
   2157                 if (operand_val == .generic_poison_type) {
   2158                     // The pointer was generic poison - keep looking.
   2159                     cur = un_node.operand;
   2160                 } else {
   2161                     // This must be an anyopaque pointer!
   2162                     return .{ .anyopaque_ptr = block.nodeOffset(un_node.src_node) };
   2163                 }
   2164             },
   2165             .call, .field_call => {
   2166                 // A function call can never return generic poison, so we must be
   2167                 // evaluating an `anytype` function parameter.
   2168                 // TODO: better source location - function decl rather than call
   2169                 const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   2170                 return .{ .anytype_param = block.nodeOffset(pl_node.src_node) };
   2171             },
   2172             else => return .unknown,
   2173         }
   2174     }
   2175 }
   2176 
   2177 fn analyzeAsType(
   2178     sema: *Sema,
   2179     block: *Block,
   2180     src: LazySrcLoc,
   2181     air_inst: Air.Inst.Ref,
   2182 ) !Type {
   2183     const wanted_type: Type = .type;
   2184     const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
   2185     const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{ .simple = .type });
   2186     return val.toType();
   2187 }
   2188 
   2189 pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
   2190     const pt = sema.pt;
   2191     const zcu = pt.zcu;
   2192     const comp = zcu.comp;
   2193     const gpa = sema.gpa;
   2194     const ip = &zcu.intern_pool;
   2195     if (!comp.config.any_error_tracing) return;
   2196 
   2197     assert(!block.isComptime());
   2198     var err_trace_block = block.makeSubBlock();
   2199     defer err_trace_block.instructions.deinit(gpa);
   2200 
   2201     const src: LazySrcLoc = LazySrcLoc.unneeded;
   2202 
   2203     // var addrs: [err_return_trace_addr_count]usize = undefined;
   2204     const err_return_trace_addr_count = 32;
   2205     const addr_arr_ty = try pt.arrayType(.{
   2206         .len = err_return_trace_addr_count,
   2207         .child = .usize_type,
   2208     });
   2209     const addrs_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(addr_arr_ty));
   2210 
   2211     // var st: StackTrace = undefined;
   2212     const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
   2213     try stack_trace_ty.resolveFields(pt);
   2214     const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty));
   2215 
   2216     // st.instruction_addresses = &addrs;
   2217     const instruction_addresses_field_name = try ip.getOrPutString(gpa, pt.tid, "instruction_addresses", .no_embedded_nulls);
   2218     const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true);
   2219     try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
   2220 
   2221     // st.index = 0;
   2222     const index_field_name = try ip.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
   2223     const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true);
   2224     try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store);
   2225 
   2226     // @errorReturnTrace() = &st;
   2227     _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
   2228 
   2229     try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items);
   2230 }
   2231 
   2232 /// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value.
   2233 fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
   2234     const zcu = sema.pt.zcu;
   2235     assert(inst != .none);
   2236 
   2237     if (try sema.typeHasOnePossibleValue(sema.typeOf(inst))) |opv| {
   2238         return opv;
   2239     }
   2240 
   2241     if (inst.toInterned()) |ip_index| {
   2242         const val: Value = .fromInterned(ip_index);
   2243         assert(val.getVariable(zcu) == null);
   2244         return val;
   2245     } else {
   2246         // Runtime-known value.
   2247         const air_tags = sema.air_instructions.items(.tag);
   2248         switch (air_tags[@intFromEnum(inst.toIndex().?)]) {
   2249             .inferred_alloc => unreachable, // assertion failure
   2250             .inferred_alloc_comptime => unreachable, // assertion failure
   2251             else => {},
   2252         }
   2253         return null;
   2254     }
   2255 }
   2256 
   2257 /// Like `resolveValue`, but emits an error if the value is not comptime-known.
   2258 fn resolveConstValue(
   2259     sema: *Sema,
   2260     block: *Block,
   2261     src: LazySrcLoc,
   2262     inst: Air.Inst.Ref,
   2263     reason: ?ComptimeReason,
   2264 ) CompileError!Value {
   2265     return try sema.resolveValue(inst) orelse {
   2266         return sema.failWithNeededComptime(block, src, reason);
   2267     };
   2268 }
   2269 
   2270 /// Like `resolveValue`, but emits an error if the value is comptime-known to be undefined.
   2271 fn resolveDefinedValue(
   2272     sema: *Sema,
   2273     block: *Block,
   2274     src: LazySrcLoc,
   2275     air_ref: Air.Inst.Ref,
   2276 ) CompileError!?Value {
   2277     const pt = sema.pt;
   2278     const zcu = pt.zcu;
   2279     const val = try sema.resolveValue(air_ref) orelse return null;
   2280     if (val.isUndef(zcu)) {
   2281         return sema.failWithUseOfUndef(block, src);
   2282     }
   2283     return val;
   2284 }
   2285 
   2286 /// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined.
   2287 fn resolveConstDefinedValue(
   2288     sema: *Sema,
   2289     block: *Block,
   2290     src: LazySrcLoc,
   2291     air_ref: Air.Inst.Ref,
   2292     reason: ?ComptimeReason,
   2293 ) CompileError!Value {
   2294     const val = try sema.resolveConstValue(block, src, air_ref, reason);
   2295     if (val.isUndef(sema.pt.zcu)) return sema.failWithUseOfUndef(block, src);
   2296     return val;
   2297 }
   2298 
   2299 /// Like `resolveValue`, but recursively resolves lazy values before returning.
   2300 fn resolveValueResolveLazy(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Value {
   2301     return try sema.resolveLazyValue((try sema.resolveValue(inst)) orelse return null);
   2302 }
   2303 
   2304 /// Value Tag may be `undef` or `variable`.
   2305 pub fn resolveFinalDeclValue(
   2306     sema: *Sema,
   2307     block: *Block,
   2308     src: LazySrcLoc,
   2309     air_ref: Air.Inst.Ref,
   2310 ) CompileError!Value {
   2311     const zcu = sema.pt.zcu;
   2312     const val = try sema.resolveConstValue(block, src, air_ref, .{ .simple = .container_var_init });
   2313     if (val.canMutateComptimeVarState(zcu)) {
   2314         const ip = &zcu.intern_pool;
   2315         const nav = ip.getNav(sema.owner.unwrap().nav_val);
   2316         return sema.failWithContainsReferenceToComptimeVar(block, src, nav.name, "global variable", val);
   2317     }
   2318     return val;
   2319 }
   2320 
   2321 fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: ?ComptimeReason) CompileError {
   2322     const msg, const fail_block = msg: {
   2323         const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
   2324         errdefer msg.destroy(sema.gpa);
   2325         const fail_block = if (reason) |r| b: {
   2326             try r.explain(sema, src, msg);
   2327             break :b block;
   2328         } else b: {
   2329             break :b try block.explainWhyBlockIsComptime(msg);
   2330         };
   2331         break :msg .{ msg, fail_block };
   2332     };
   2333     return sema.failWithOwnedErrorMsg(fail_block, msg);
   2334 }
   2335 
   2336 pub fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2337     return sema.fail(block, src, "use of undefined value here causes illegal behavior", .{});
   2338 }
   2339 
   2340 pub fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2341     return sema.fail(block, src, "division by zero here causes illegal behavior", .{});
   2342 }
   2343 
   2344 fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError {
   2345     const pt = sema.pt;
   2346     return sema.fail(block, src, "remainder division with '{f}' and '{f}': signed integers and floats must use @rem or @mod", .{
   2347         lhs_ty.fmt(pt), rhs_ty.fmt(pt),
   2348     });
   2349 }
   2350 
   2351 fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError {
   2352     const pt = sema.pt;
   2353     const msg = msg: {
   2354         const msg = try sema.errMsg(src, "expected optional type, found '{f}'", .{
   2355             non_optional_ty.fmt(pt),
   2356         });
   2357         errdefer msg.destroy(sema.gpa);
   2358         if (non_optional_ty.zigTypeTag(pt.zcu) == .error_union) {
   2359             try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   2360         }
   2361         try addDeclaredHereNote(sema, msg, non_optional_ty);
   2362         break :msg msg;
   2363     };
   2364     return sema.failWithOwnedErrorMsg(block, msg);
   2365 }
   2366 
   2367 fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
   2368     const pt = sema.pt;
   2369     const msg = msg: {
   2370         const msg = try sema.errMsg(src, "type '{f}' does not support array initialization syntax", .{
   2371             ty.fmt(pt),
   2372         });
   2373         errdefer msg.destroy(sema.gpa);
   2374         if (ty.isSlice(pt.zcu)) {
   2375             try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.elemType2(pt.zcu).fmt(pt)});
   2376         }
   2377         break :msg msg;
   2378     };
   2379     return sema.failWithOwnedErrorMsg(block, msg);
   2380 }
   2381 
   2382 fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
   2383     const pt = sema.pt;
   2384     return sema.fail(block, src, "type '{f}' does not support struct initialization syntax", .{
   2385         ty.fmt(pt),
   2386     });
   2387 }
   2388 
   2389 fn failWithErrorSetCodeMissing(
   2390     sema: *Sema,
   2391     block: *Block,
   2392     src: LazySrcLoc,
   2393     dest_err_set_ty: Type,
   2394     src_err_set_ty: Type,
   2395 ) CompileError {
   2396     const pt = sema.pt;
   2397     return sema.fail(block, src, "expected type '{f}', found type '{f}'", .{
   2398         dest_err_set_ty.fmt(pt), src_err_set_ty.fmt(pt),
   2399     });
   2400 }
   2401 
   2402 pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError {
   2403     const pt = sema.pt;
   2404     return sema.failWithOwnedErrorMsg(block, msg: {
   2405         const msg = try sema.errMsg(src, "overflow of integer type '{f}' with value '{f}'", .{
   2406             int_ty.fmt(pt), val.fmtValueSema(pt, sema),
   2407         });
   2408         errdefer msg.destroy(sema.gpa);
   2409         if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i});
   2410         break :msg msg;
   2411     });
   2412 }
   2413 
   2414 fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError {
   2415     const pt = sema.pt;
   2416     const zcu = pt.zcu;
   2417     const msg = msg: {
   2418         const msg = try sema.errMsg(init_src, "value stored in comptime field does not match the default value of the field", .{});
   2419         errdefer msg.destroy(sema.gpa);
   2420 
   2421         const struct_type = zcu.typeToStruct(container_ty) orelse break :msg msg;
   2422         try sema.errNote(.{
   2423             .base_node_inst = struct_type.zir_index,
   2424             .offset = .{ .container_field_value = @intCast(field_index) },
   2425         }, msg, "default value set here", .{});
   2426         break :msg msg;
   2427     };
   2428     return sema.failWithOwnedErrorMsg(block, msg);
   2429 }
   2430 
   2431 fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
   2432     const msg = msg: {
   2433         const msg = try sema.errMsg(src, "async has not been implemented in the self-hosted compiler yet", .{});
   2434         errdefer msg.destroy(sema.gpa);
   2435         break :msg msg;
   2436     };
   2437     return sema.failWithOwnedErrorMsg(block, msg);
   2438 }
   2439 
   2440 fn failWithInvalidFieldAccess(
   2441     sema: *Sema,
   2442     block: *Block,
   2443     src: LazySrcLoc,
   2444     object_ty: Type,
   2445     field_name: InternPool.NullTerminatedString,
   2446 ) CompileError {
   2447     const pt = sema.pt;
   2448     const zcu = pt.zcu;
   2449     const inner_ty = if (object_ty.isSinglePointer(zcu)) object_ty.childType(zcu) else object_ty;
   2450 
   2451     if (inner_ty.zigTypeTag(zcu) == .optional) opt: {
   2452         const child_ty = inner_ty.optionalChild(zcu);
   2453         if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :opt;
   2454         const msg = msg: {
   2455             const msg = try sema.errMsg(src, "optional type '{f}' does not support field access", .{object_ty.fmt(pt)});
   2456             errdefer msg.destroy(sema.gpa);
   2457             try sema.errNote(src, msg, "consider using '.?', 'orelse', or 'if'", .{});
   2458             break :msg msg;
   2459         };
   2460         return sema.failWithOwnedErrorMsg(block, msg);
   2461     } else if (inner_ty.zigTypeTag(zcu) == .error_union) err: {
   2462         const child_ty = inner_ty.errorUnionPayload(zcu);
   2463         if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :err;
   2464         const msg = msg: {
   2465             const msg = try sema.errMsg(src, "error union type '{f}' does not support field access", .{object_ty.fmt(pt)});
   2466             errdefer msg.destroy(sema.gpa);
   2467             try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   2468             break :msg msg;
   2469         };
   2470         return sema.failWithOwnedErrorMsg(block, msg);
   2471     }
   2472     return sema.fail(block, src, "type '{f}' does not support field access", .{object_ty.fmt(pt)});
   2473 }
   2474 
   2475 fn typeSupportsFieldAccess(zcu: *const Zcu, ty: Type, field_name: InternPool.NullTerminatedString) bool {
   2476     const ip = &zcu.intern_pool;
   2477     switch (ty.zigTypeTag(zcu)) {
   2478         .array => return field_name.eqlSlice("len", ip),
   2479         .pointer => {
   2480             const ptr_info = ty.ptrInfo(zcu);
   2481             if (ptr_info.flags.size == .slice) {
   2482                 return field_name.eqlSlice("ptr", ip) or field_name.eqlSlice("len", ip);
   2483             } else if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) {
   2484                 return field_name.eqlSlice("len", ip);
   2485             } else return false;
   2486         },
   2487         .type, .@"struct", .@"union" => return true,
   2488         else => return false,
   2489     }
   2490 }
   2491 
   2492 fn failWithComptimeErrorRetTrace(
   2493     sema: *Sema,
   2494     block: *Block,
   2495     src: LazySrcLoc,
   2496     name: InternPool.NullTerminatedString,
   2497 ) CompileError {
   2498     const pt = sema.pt;
   2499     const zcu = pt.zcu;
   2500     const msg = msg: {
   2501         const msg = try sema.errMsg(src, "caught unexpected error '{f}'", .{name.fmt(&zcu.intern_pool)});
   2502         errdefer msg.destroy(sema.gpa);
   2503 
   2504         for (sema.comptime_err_ret_trace.items) |src_loc| {
   2505             try sema.errNote(src_loc, msg, "error returned here", .{});
   2506         }
   2507         break :msg msg;
   2508     };
   2509     return sema.failWithOwnedErrorMsg(block, msg);
   2510 }
   2511 
   2512 fn failWithInvalidPtrArithmetic(sema: *Sema, block: *Block, src: LazySrcLoc, arithmetic: []const u8, supports: []const u8) CompileError {
   2513     const msg = msg: {
   2514         const msg = try sema.errMsg(src, "invalid {s} arithmetic operator", .{arithmetic});
   2515         errdefer msg.destroy(sema.gpa);
   2516         try sema.errNote(src, msg, "{s} arithmetic only supports {s}", .{ arithmetic, supports });
   2517         break :msg msg;
   2518     };
   2519     return sema.failWithOwnedErrorMsg(block, msg);
   2520 }
   2521 
   2522 /// We don't return a pointer to the new error note because the pointer
   2523 /// becomes invalid when you add another one.
   2524 pub fn errNote(
   2525     sema: *Sema,
   2526     src: LazySrcLoc,
   2527     parent: *Zcu.ErrorMsg,
   2528     comptime format: []const u8,
   2529     args: anytype,
   2530 ) error{OutOfMemory}!void {
   2531     return sema.pt.zcu.errNote(src, parent, format, args);
   2532 }
   2533 
   2534 fn addFieldErrNote(
   2535     sema: *Sema,
   2536     container_ty: Type,
   2537     field_index: usize,
   2538     parent: *Zcu.ErrorMsg,
   2539     comptime format: []const u8,
   2540     args: anytype,
   2541 ) !void {
   2542     @branchHint(.cold);
   2543     const type_src = container_ty.srcLocOrNull(sema.pt.zcu) orelse return;
   2544     const field_src: LazySrcLoc = .{
   2545         .base_node_inst = type_src.base_node_inst,
   2546         .offset = .{ .container_field_name = @intCast(field_index) },
   2547     };
   2548     try sema.errNote(field_src, parent, format, args);
   2549 }
   2550 
   2551 pub fn errMsg(
   2552     sema: *Sema,
   2553     src: LazySrcLoc,
   2554     comptime format: []const u8,
   2555     args: anytype,
   2556 ) Allocator.Error!*Zcu.ErrorMsg {
   2557     assert(src.offset != .unneeded);
   2558     return Zcu.ErrorMsg.create(sema.gpa, src, format, args);
   2559 }
   2560 
   2561 pub fn fail(
   2562     sema: *Sema,
   2563     block: *Block,
   2564     src: LazySrcLoc,
   2565     comptime format: []const u8,
   2566     args: anytype,
   2567 ) CompileError {
   2568     const err_msg = try sema.errMsg(src, format, args);
   2569     inline for (args) |arg| {
   2570         if (@TypeOf(arg) == Type.Formatter) {
   2571             try addDeclaredHereNote(sema, err_msg, arg.data.ty);
   2572         }
   2573     }
   2574     return sema.failWithOwnedErrorMsg(block, err_msg);
   2575 }
   2576 
   2577 pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) error{ AnalysisFail, OutOfMemory } {
   2578     @branchHint(.cold);
   2579     const gpa = sema.gpa;
   2580     const zcu = sema.pt.zcu;
   2581 
   2582     if (build_options.enable_debug_extensions and zcu.comp.debug_compile_errors) {
   2583         var wip_errors: std.zig.ErrorBundle.Wip = undefined;
   2584         wip_errors.init(gpa) catch @panic("out of memory");
   2585         Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory");
   2586         std.debug.print("compile error during Sema:\n", .{});
   2587         var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory");
   2588         error_bundle.renderToStdErr(.{ .ttyconf = .no_color });
   2589         crash_report.compilerPanic("unexpected compile error occurred", null);
   2590     }
   2591 
   2592     if (block) |start_block| {
   2593         var block_it = start_block;
   2594         while (block_it.inlining) |inlining| {
   2595             const note_str = note: {
   2596                 if (inlining.is_generic_instantiation) break :note "generic function instantiated here";
   2597                 if (inlining.call_block.isComptime()) break :note "called at comptime here";
   2598                 break :note "called inline here";
   2599             };
   2600             try sema.errNote(inlining.call_src, err_msg, "{s}", .{note_str});
   2601             block_it = inlining.call_block;
   2602         }
   2603     }
   2604 
   2605     err_msg.reference_trace_root = sema.owner.toOptional();
   2606 
   2607     const gop = try zcu.failed_analysis.getOrPut(gpa, sema.owner);
   2608     if (gop.found_existing) {
   2609         // If there are multiple errors for the same Decl, prefer the first one added.
   2610         sema.err = null;
   2611         err_msg.destroy(gpa);
   2612     } else {
   2613         sema.err = err_msg;
   2614         gop.value_ptr.* = err_msg;
   2615     }
   2616 
   2617     return error.AnalysisFail;
   2618 }
   2619 
   2620 /// Given an ErrorMsg, modify its message and source location to the given values, turning the
   2621 /// original message into a note. Notes on the original message are preserved as further notes.
   2622 /// Reference trace is preserved.
   2623 fn reparentOwnedErrorMsg(
   2624     sema: *Sema,
   2625     src: LazySrcLoc,
   2626     msg: *Zcu.ErrorMsg,
   2627     comptime format: []const u8,
   2628     args: anytype,
   2629 ) !void {
   2630     const msg_str = try std.fmt.allocPrint(sema.gpa, format, args);
   2631 
   2632     const orig_notes = msg.notes.len;
   2633     msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1);
   2634     std.mem.copyBackwards(Zcu.ErrorMsg, msg.notes[1..], msg.notes[0..orig_notes]);
   2635     msg.notes[0] = .{
   2636         .src_loc = msg.src_loc,
   2637         .msg = msg.msg,
   2638     };
   2639 
   2640     msg.src_loc = src;
   2641     msg.msg = msg_str;
   2642 }
   2643 
   2644 const align_ty: Type = .u29;
   2645 
   2646 pub fn analyzeAsAlign(
   2647     sema: *Sema,
   2648     block: *Block,
   2649     src: LazySrcLoc,
   2650     air_ref: Air.Inst.Ref,
   2651 ) !Alignment {
   2652     const alignment_big = try sema.analyzeAsInt(
   2653         block,
   2654         src,
   2655         air_ref,
   2656         align_ty,
   2657         .{ .simple = .@"align" },
   2658     );
   2659     return sema.validateAlign(block, src, alignment_big);
   2660 }
   2661 
   2662 fn validateAlign(
   2663     sema: *Sema,
   2664     block: *Block,
   2665     src: LazySrcLoc,
   2666     alignment: u64,
   2667 ) !Alignment {
   2668     if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{});
   2669     if (!std.math.isPowerOfTwo(alignment)) {
   2670         return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{
   2671             alignment,
   2672         });
   2673     }
   2674     return Alignment.fromNonzeroByteUnits(alignment);
   2675 }
   2676 
   2677 fn resolveAlign(
   2678     sema: *Sema,
   2679     block: *Block,
   2680     src: LazySrcLoc,
   2681     zir_ref: Zir.Inst.Ref,
   2682 ) !Alignment {
   2683     const air_ref = try sema.resolveInst(zir_ref);
   2684     return sema.analyzeAsAlign(block, src, air_ref);
   2685 }
   2686 
   2687 fn resolveInt(
   2688     sema: *Sema,
   2689     block: *Block,
   2690     src: LazySrcLoc,
   2691     zir_ref: Zir.Inst.Ref,
   2692     dest_ty: Type,
   2693     reason: ComptimeReason,
   2694 ) !u64 {
   2695     const air_ref = try sema.resolveInst(zir_ref);
   2696     return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason);
   2697 }
   2698 
   2699 fn analyzeAsInt(
   2700     sema: *Sema,
   2701     block: *Block,
   2702     src: LazySrcLoc,
   2703     air_ref: Air.Inst.Ref,
   2704     dest_ty: Type,
   2705     reason: ComptimeReason,
   2706 ) !u64 {
   2707     const coerced = try sema.coerce(block, dest_ty, air_ref, src);
   2708     const val = try sema.resolveConstDefinedValue(block, src, coerced, reason);
   2709     return try val.toUnsignedIntSema(sema.pt);
   2710 }
   2711 
   2712 fn analyzeValueAsCallconv(
   2713     sema: *Sema,
   2714     block: *Block,
   2715     src: LazySrcLoc,
   2716     unresolved_val: Value,
   2717 ) !std.builtin.CallingConvention {
   2718     return interpretBuiltinType(sema, block, src, unresolved_val, std.builtin.CallingConvention);
   2719 }
   2720 
   2721 fn interpretBuiltinType(
   2722     sema: *Sema,
   2723     block: *Block,
   2724     src: LazySrcLoc,
   2725     unresolved_val: Value,
   2726     comptime T: type,
   2727 ) !T {
   2728     const resolved_val = try sema.resolveLazyValue(unresolved_val);
   2729     return resolved_val.interpret(T, sema.pt) catch |err| switch (err) {
   2730         error.OutOfMemory => |e| return e,
   2731         error.UndefinedValue => return sema.failWithUseOfUndef(block, src),
   2732         error.TypeMismatch => @panic("std.builtin is corrupt"),
   2733     };
   2734 }
   2735 
   2736 fn zirTupleDecl(
   2737     sema: *Sema,
   2738     block: *Block,
   2739     extended: Zir.Inst.Extended.InstData,
   2740 ) CompileError!Air.Inst.Ref {
   2741     const gpa = sema.gpa;
   2742     const pt = sema.pt;
   2743     const zcu = pt.zcu;
   2744     const fields_len = extended.small;
   2745     const extra = sema.code.extraData(Zir.Inst.TupleDecl, extended.operand);
   2746     var extra_index = extra.end;
   2747 
   2748     const types = try sema.arena.alloc(InternPool.Index, fields_len);
   2749     const inits = try sema.arena.alloc(InternPool.Index, fields_len);
   2750 
   2751     const extra_as_refs: []const Zir.Inst.Ref = @ptrCast(sema.code.extra);
   2752 
   2753     for (types, inits, 0..) |*field_ty, *field_init, field_index| {
   2754         const zir_field_ty, const zir_field_init = extra_as_refs[extra_index..][0..2].*;
   2755         extra_index += 2;
   2756 
   2757         const type_src = block.src(.{ .tuple_field_type = .{
   2758             .tuple_decl_node_offset = extra.data.src_node,
   2759             .elem_index = @intCast(field_index),
   2760         } });
   2761         const init_src = block.src(.{ .tuple_field_init = .{
   2762             .tuple_decl_node_offset = extra.data.src_node,
   2763             .elem_index = @intCast(field_index),
   2764         } });
   2765 
   2766         const field_type = try sema.resolveType(block, type_src, zir_field_ty);
   2767         try sema.validateTupleFieldType(block, field_type, type_src);
   2768 
   2769         field_ty.* = field_type.toIntern();
   2770         field_init.* = init: {
   2771             if (zir_field_init != .none) {
   2772                 const uncoerced_field_init = try sema.resolveInst(zir_field_init);
   2773                 const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src);
   2774                 const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value });
   2775                 if (field_init_val.canMutateComptimeVarState(zcu)) {
   2776                     const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
   2777                     return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val);
   2778                 }
   2779                 break :init field_init_val.toIntern();
   2780             }
   2781             if (try sema.typeHasOnePossibleValue(field_type)) |opv| {
   2782                 break :init opv.toIntern();
   2783             }
   2784             break :init .none;
   2785         };
   2786     }
   2787 
   2788     return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{
   2789         .types = types,
   2790         .values = inits,
   2791     }));
   2792 }
   2793 
   2794 fn validateTupleFieldType(
   2795     sema: *Sema,
   2796     block: *Block,
   2797     field_ty: Type,
   2798     field_ty_src: LazySrcLoc,
   2799 ) CompileError!void {
   2800     const gpa = sema.gpa;
   2801     const zcu = sema.pt.zcu;
   2802     if (field_ty.zigTypeTag(zcu) == .@"opaque") {
   2803         return sema.failWithOwnedErrorMsg(block, msg: {
   2804             const msg = try sema.errMsg(field_ty_src, "opaque types have unknown size and therefore cannot be directly embedded in tuples", .{});
   2805             errdefer msg.destroy(gpa);
   2806 
   2807             try sema.addDeclaredHereNote(msg, field_ty);
   2808             break :msg msg;
   2809         });
   2810     }
   2811     if (field_ty.zigTypeTag(zcu) == .noreturn) {
   2812         return sema.failWithOwnedErrorMsg(block, msg: {
   2813             const msg = try sema.errMsg(field_ty_src, "tuple fields cannot be 'noreturn'", .{});
   2814             errdefer msg.destroy(gpa);
   2815 
   2816             try sema.addDeclaredHereNote(msg, field_ty);
   2817             break :msg msg;
   2818         });
   2819     }
   2820 }
   2821 
   2822 /// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`,
   2823 /// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`.
   2824 fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: usize, captures_len: u32) ![]InternPool.CaptureValue {
   2825     const pt = sema.pt;
   2826     const zcu = pt.zcu;
   2827     const ip = &zcu.intern_pool;
   2828     const parent_ty: Type = .fromInterned(zcu.namespacePtr(block.namespace).owner_type);
   2829     const parent_captures: InternPool.CaptureValue.Slice = parent_ty.getCaptures(zcu);
   2830 
   2831     const captures = try sema.arena.alloc(InternPool.CaptureValue, captures_len);
   2832 
   2833     for (sema.code.extra[extra_index..][0..captures_len], sema.code.extra[extra_index + captures_len ..][0..captures_len], captures) |raw, raw_name, *capture| {
   2834         const zir_capture: Zir.Inst.Capture = @bitCast(raw);
   2835         const zir_name: Zir.NullTerminatedString = @enumFromInt(raw_name);
   2836         const zir_name_slice = sema.code.nullTerminatedString(zir_name);
   2837         capture.* = switch (zir_capture.unwrap()) {
   2838             .nested => |parent_idx| parent_captures.get(ip)[parent_idx],
   2839             .instruction_load => |ptr_inst| InternPool.CaptureValue.wrap(capture: {
   2840                 const ptr_ref = try sema.resolveInst(ptr_inst.toRef());
   2841                 const ptr_val = try sema.resolveValue(ptr_ref) orelse {
   2842                     break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() };
   2843                 };
   2844                 // TODO: better source location
   2845                 const unresolved_loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse {
   2846                     break :capture .{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() };
   2847                 };
   2848                 const loaded_val = try sema.resolveLazyValue(unresolved_loaded_val);
   2849                 if (loaded_val.canMutateComptimeVarState(zcu)) {
   2850                     const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls);
   2851                     return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", loaded_val);
   2852                 }
   2853                 break :capture .{ .@"comptime" = loaded_val.toIntern() };
   2854             }),
   2855             .instruction => |inst| InternPool.CaptureValue.wrap(capture: {
   2856                 const air_ref = try sema.resolveInst(inst.toRef());
   2857                 if (try sema.resolveValueResolveLazy(air_ref)) |val| {
   2858                     if (val.canMutateComptimeVarState(zcu)) {
   2859                         const field_name = try ip.getOrPutString(zcu.gpa, pt.tid, zir_name_slice, .no_embedded_nulls);
   2860                         return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", val);
   2861                     }
   2862                     break :capture .{ .@"comptime" = val.toIntern() };
   2863                 }
   2864                 break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() };
   2865             }),
   2866             .decl_val => |str| capture: {
   2867                 const decl_name = try ip.getOrPutString(
   2868                     sema.gpa,
   2869                     pt.tid,
   2870                     sema.code.nullTerminatedString(str),
   2871                     .no_embedded_nulls,
   2872                 );
   2873                 const nav = try sema.lookupIdentifier(block, decl_name);
   2874                 break :capture InternPool.CaptureValue.wrap(.{ .nav_val = nav });
   2875             },
   2876             .decl_ref => |str| capture: {
   2877                 const decl_name = try ip.getOrPutString(
   2878                     sema.gpa,
   2879                     pt.tid,
   2880                     sema.code.nullTerminatedString(str),
   2881                     .no_embedded_nulls,
   2882                 );
   2883                 const nav = try sema.lookupIdentifier(block, decl_name);
   2884                 break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav });
   2885             },
   2886         };
   2887     }
   2888 
   2889     return captures;
   2890 }
   2891 
   2892 fn zirStructDecl(
   2893     sema: *Sema,
   2894     block: *Block,
   2895     extended: Zir.Inst.Extended.InstData,
   2896     inst: Zir.Inst.Index,
   2897 ) CompileError!Air.Inst.Ref {
   2898     const pt = sema.pt;
   2899     const zcu = pt.zcu;
   2900     const gpa = sema.gpa;
   2901     const ip = &zcu.intern_pool;
   2902     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
   2903     const extra = sema.code.extraData(Zir.Inst.StructDecl, extended.operand);
   2904 
   2905     const tracked_inst = try block.trackZir(inst);
   2906     const src: LazySrcLoc = .{
   2907         .base_node_inst = tracked_inst,
   2908         .offset = LazySrcLoc.Offset.nodeOffset(.zero),
   2909     };
   2910 
   2911     var extra_index = extra.end;
   2912 
   2913     const captures_len = if (small.has_captures_len) blk: {
   2914         const captures_len = sema.code.extra[extra_index];
   2915         extra_index += 1;
   2916         break :blk captures_len;
   2917     } else 0;
   2918     const fields_len = if (small.has_fields_len) blk: {
   2919         const fields_len = sema.code.extra[extra_index];
   2920         extra_index += 1;
   2921         break :blk fields_len;
   2922     } else 0;
   2923     const decls_len = if (small.has_decls_len) blk: {
   2924         const decls_len = sema.code.extra[extra_index];
   2925         extra_index += 1;
   2926         break :blk decls_len;
   2927     } else 0;
   2928 
   2929     const captures = try sema.getCaptures(block, src, extra_index, captures_len);
   2930     extra_index += captures_len * 2;
   2931 
   2932     if (small.has_backing_int) {
   2933         const backing_int_body_len = sema.code.extra[extra_index];
   2934         extra_index += 1; // backing_int_body_len
   2935         if (backing_int_body_len == 0) {
   2936             extra_index += 1; // backing_int_ref
   2937         } else {
   2938             extra_index += backing_int_body_len; // backing_int_body_inst
   2939         }
   2940     }
   2941 
   2942     const struct_init: InternPool.StructTypeInit = .{
   2943         .layout = small.layout,
   2944         .fields_len = fields_len,
   2945         .known_non_opv = small.known_non_opv,
   2946         .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
   2947         .any_comptime_fields = small.any_comptime_fields,
   2948         .any_default_inits = small.any_default_inits,
   2949         .inits_resolved = false,
   2950         .any_aligned_fields = small.any_aligned_fields,
   2951         .key = .{ .declared = .{
   2952             .zir_index = tracked_inst,
   2953             .captures = captures,
   2954         } },
   2955     };
   2956     const wip_ty = switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) {
   2957         .existing => |ty| {
   2958             const new_ty = try pt.ensureTypeUpToDate(ty);
   2959 
   2960             // Make sure we update the namespace if the declaration is re-analyzed, to pick
   2961             // up on e.g. changed comptime decls.
   2962             try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu));
   2963 
   2964             try sema.declareDependency(.{ .interned = new_ty });
   2965             try sema.addTypeReferenceEntry(src, new_ty);
   2966             return Air.internedToRef(new_ty);
   2967         },
   2968         .wip => |wip| wip,
   2969     };
   2970     errdefer wip_ty.cancel(ip, pt.tid);
   2971 
   2972     const type_name = try sema.createTypeName(
   2973         block,
   2974         small.name_strategy,
   2975         "struct",
   2976         inst,
   2977         wip_ty.index,
   2978     );
   2979     wip_ty.setName(ip, type_name.name, type_name.nav);
   2980 
   2981     const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
   2982         .parent = block.namespace.toOptional(),
   2983         .owner_type = wip_ty.index,
   2984         .file_scope = block.getFileScopeIndex(zcu),
   2985         .generation = zcu.generation,
   2986     });
   2987     errdefer pt.destroyNamespace(new_namespace_index);
   2988 
   2989     if (pt.zcu.comp.incremental) {
   2990         try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
   2991     }
   2992 
   2993     const decls = sema.code.bodySlice(extra_index, decls_len);
   2994     try pt.scanNamespace(new_namespace_index, decls);
   2995 
   2996     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
   2997     codegen_type: {
   2998         if (zcu.comp.config.use_llvm) break :codegen_type;
   2999         if (block.ownerModule().strip) break :codegen_type;
   3000         // This job depends on any resolve_type_fully jobs queued up before it.
   3001         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   3002         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   3003     }
   3004     try sema.declareDependency(.{ .interned = wip_ty.index });
   3005     try sema.addTypeReferenceEntry(src, wip_ty.index);
   3006     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   3007     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
   3008 }
   3009 
   3010 pub fn createTypeName(
   3011     sema: *Sema,
   3012     block: *Block,
   3013     name_strategy: Zir.Inst.NameStrategy,
   3014     anon_prefix: []const u8,
   3015     inst: ?Zir.Inst.Index,
   3016     /// This is used purely to give the type a unique name in the `anon` case.
   3017     type_index: InternPool.Index,
   3018 ) CompileError!struct {
   3019     name: InternPool.NullTerminatedString,
   3020     nav: InternPool.Nav.Index.Optional,
   3021 } {
   3022     const pt = sema.pt;
   3023     const zcu = pt.zcu;
   3024     const gpa = zcu.gpa;
   3025     const ip = &zcu.intern_pool;
   3026 
   3027     switch (name_strategy) {
   3028         .anon => {}, // handled after switch
   3029         .parent => return .{
   3030             .name = block.type_name_ctx,
   3031             .nav = sema.owner.unwrap().nav_val.toOptional(),
   3032         },
   3033         .func => func_strat: {
   3034             const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse return error.AnalysisFail);
   3035             const zir_tags = sema.code.instructions.items(.tag);
   3036 
   3037             var aw: std.io.Writer.Allocating = .init(gpa);
   3038             defer aw.deinit();
   3039             const w = &aw.writer;
   3040             w.print("{f}(", .{block.type_name_ctx.fmt(ip)}) catch return error.OutOfMemory;
   3041 
   3042             var arg_i: usize = 0;
   3043             for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) {
   3044                 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
   3045                     const arg = sema.inst_map.get(zir_inst).?;
   3046                     // If this is being called in a generic function then analyzeCall will
   3047                     // have already resolved the args and this will work.
   3048                     // If not then this is a struct type being returned from a non-generic
   3049                     // function and the name doesn't matter since it will later
   3050                     // result in a compile error.
   3051                     const arg_val = try sema.resolveValue(arg) orelse break :func_strat; // fall through to anon strat
   3052 
   3053                     if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory;
   3054 
   3055                     // Limiting the depth here helps avoid type names getting too long, which
   3056                     // in turn helps to avoid unreasonably long symbol names for namespaced
   3057                     // symbols. Such names should ideally be human-readable, and additionally,
   3058                     // some tooling may not support very long symbol names.
   3059                     w.print("{f}", .{Value.fmtValueSemaFull(.{
   3060                         .val = arg_val,
   3061                         .pt = pt,
   3062                         .opt_sema = sema,
   3063                         .depth = 1,
   3064                     })}) catch return error.OutOfMemory;
   3065 
   3066                     arg_i += 1;
   3067                     continue;
   3068                 },
   3069                 else => continue,
   3070             };
   3071 
   3072             w.writeByte(')') catch return error.OutOfMemory;
   3073             return .{
   3074                 .name = try ip.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls),
   3075                 .nav = .none,
   3076             };
   3077         },
   3078         .dbg_var => {
   3079             // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions.
   3080             const ref = inst.?.toRef();
   3081             const zir_tags = sema.code.instructions.items(.tag);
   3082             const zir_data = sema.code.instructions.items(.data);
   3083             for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) {
   3084                 .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) {
   3085                     return .{
   3086                         .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}.{s}", .{
   3087                             block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code),
   3088                         }, .no_embedded_nulls),
   3089                         .nav = .none,
   3090                     };
   3091                 },
   3092                 else => {},
   3093             };
   3094             // fall through to anon strat
   3095         },
   3096     }
   3097 
   3098     // anon strat handling
   3099 
   3100     // It would be neat to have "struct:line:column" but this name has
   3101     // to survive incremental updates, where it may have been shifted down
   3102     // or up to a different line, but unchanged, and thus not unnecessarily
   3103     // semantically analyzed.
   3104     // TODO: that would be possible, by detecting line number changes and renaming
   3105     // types appropriately. However, `@typeName` becomes a problem then. If we remove
   3106     // that builtin from the language, we can consider this.
   3107 
   3108     return .{
   3109         .name = try ip.getOrPutStringFmt(gpa, pt.tid, "{f}__{s}_{d}", .{
   3110             block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(type_index),
   3111         }, .no_embedded_nulls),
   3112         .nav = .none,
   3113     };
   3114 }
   3115 
   3116 fn zirEnumDecl(
   3117     sema: *Sema,
   3118     block: *Block,
   3119     extended: Zir.Inst.Extended.InstData,
   3120     inst: Zir.Inst.Index,
   3121 ) CompileError!Air.Inst.Ref {
   3122     const tracy = trace(@src());
   3123     defer tracy.end();
   3124 
   3125     const pt = sema.pt;
   3126     const zcu = pt.zcu;
   3127     const gpa = sema.gpa;
   3128     const ip = &zcu.intern_pool;
   3129     const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
   3130     const extra = sema.code.extraData(Zir.Inst.EnumDecl, extended.operand);
   3131     var extra_index: usize = extra.end;
   3132 
   3133     const tracked_inst = try block.trackZir(inst);
   3134     const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
   3135 
   3136     const tag_type_ref = if (small.has_tag_type) blk: {
   3137         const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   3138         extra_index += 1;
   3139         break :blk tag_type_ref;
   3140     } else .none;
   3141 
   3142     const captures_len = if (small.has_captures_len) blk: {
   3143         const captures_len = sema.code.extra[extra_index];
   3144         extra_index += 1;
   3145         break :blk captures_len;
   3146     } else 0;
   3147 
   3148     const body_len = if (small.has_body_len) blk: {
   3149         const body_len = sema.code.extra[extra_index];
   3150         extra_index += 1;
   3151         break :blk body_len;
   3152     } else 0;
   3153 
   3154     const fields_len = if (small.has_fields_len) blk: {
   3155         const fields_len = sema.code.extra[extra_index];
   3156         extra_index += 1;
   3157         break :blk fields_len;
   3158     } else 0;
   3159 
   3160     const decls_len = if (small.has_decls_len) blk: {
   3161         const decls_len = sema.code.extra[extra_index];
   3162         extra_index += 1;
   3163         break :blk decls_len;
   3164     } else 0;
   3165 
   3166     const captures = try sema.getCaptures(block, src, extra_index, captures_len);
   3167     extra_index += captures_len * 2;
   3168 
   3169     const decls = sema.code.bodySlice(extra_index, decls_len);
   3170     extra_index += decls_len;
   3171 
   3172     const body = sema.code.bodySlice(extra_index, body_len);
   3173     extra_index += body.len;
   3174 
   3175     const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
   3176     const body_end = extra_index;
   3177     extra_index += bit_bags_count;
   3178 
   3179     const any_values = for (sema.code.extra[body_end..][0..bit_bags_count]) |bag| {
   3180         if (bag != 0) break true;
   3181     } else false;
   3182 
   3183     const enum_init: InternPool.EnumTypeInit = .{
   3184         .has_values = any_values,
   3185         .tag_mode = if (small.nonexhaustive)
   3186             .nonexhaustive
   3187         else if (tag_type_ref == .none)
   3188             .auto
   3189         else
   3190             .explicit,
   3191         .fields_len = fields_len,
   3192         .key = .{ .declared = .{
   3193             .zir_index = tracked_inst,
   3194             .captures = captures,
   3195         } },
   3196     };
   3197     const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) {
   3198         .existing => |ty| {
   3199             const new_ty = try pt.ensureTypeUpToDate(ty);
   3200 
   3201             // Make sure we update the namespace if the declaration is re-analyzed, to pick
   3202             // up on e.g. changed comptime decls.
   3203             try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu));
   3204 
   3205             try sema.declareDependency(.{ .interned = new_ty });
   3206             try sema.addTypeReferenceEntry(src, new_ty);
   3207 
   3208             // Since this is an enum, it has to be resolved immediately.
   3209             // `ensureTypeUpToDate` has resolved the new type if necessary.
   3210             // We just need to check for resolution failures.
   3211             const ty_unit: AnalUnit = .wrap(.{ .type = new_ty });
   3212             if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) {
   3213                 return error.AnalysisFail;
   3214             }
   3215 
   3216             return Air.internedToRef(new_ty);
   3217         },
   3218         .wip => |wip| wip,
   3219     };
   3220 
   3221     // Once this is `true`, we will not delete the decl or type even upon failure, since we
   3222     // have finished constructing the type and are in the process of analyzing it.
   3223     var done = false;
   3224 
   3225     errdefer if (!done) wip_ty.cancel(ip, pt.tid);
   3226 
   3227     const type_name = try sema.createTypeName(
   3228         block,
   3229         small.name_strategy,
   3230         "enum",
   3231         inst,
   3232         wip_ty.index,
   3233     );
   3234     wip_ty.setName(ip, type_name.name, type_name.nav);
   3235 
   3236     const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
   3237         .parent = block.namespace.toOptional(),
   3238         .owner_type = wip_ty.index,
   3239         .file_scope = block.getFileScopeIndex(zcu),
   3240         .generation = zcu.generation,
   3241     });
   3242     errdefer if (!done) pt.destroyNamespace(new_namespace_index);
   3243 
   3244     try pt.scanNamespace(new_namespace_index, decls);
   3245 
   3246     try sema.declareDependency(.{ .interned = wip_ty.index });
   3247     try sema.addTypeReferenceEntry(src, wip_ty.index);
   3248 
   3249     // We've finished the initial construction of this type, and are about to perform analysis.
   3250     // Set the namespace appropriately, and don't destroy anything on failure.
   3251     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   3252     wip_ty.prepare(ip, new_namespace_index);
   3253     done = true;
   3254 
   3255     try Sema.resolveDeclaredEnum(
   3256         pt,
   3257         wip_ty,
   3258         inst,
   3259         tracked_inst,
   3260         new_namespace_index,
   3261         type_name.name,
   3262         small,
   3263         body,
   3264         tag_type_ref,
   3265         any_values,
   3266         fields_len,
   3267         sema.code,
   3268         body_end,
   3269     );
   3270 
   3271     codegen_type: {
   3272         if (zcu.comp.config.use_llvm) break :codegen_type;
   3273         if (block.ownerModule().strip) break :codegen_type;
   3274         // This job depends on any resolve_type_fully jobs queued up before it.
   3275         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   3276         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   3277     }
   3278     return Air.internedToRef(wip_ty.index);
   3279 }
   3280 
   3281 fn zirUnionDecl(
   3282     sema: *Sema,
   3283     block: *Block,
   3284     extended: Zir.Inst.Extended.InstData,
   3285     inst: Zir.Inst.Index,
   3286 ) CompileError!Air.Inst.Ref {
   3287     const tracy = trace(@src());
   3288     defer tracy.end();
   3289 
   3290     const pt = sema.pt;
   3291     const zcu = pt.zcu;
   3292     const gpa = sema.gpa;
   3293     const ip = &zcu.intern_pool;
   3294     const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
   3295     const extra = sema.code.extraData(Zir.Inst.UnionDecl, extended.operand);
   3296     var extra_index: usize = extra.end;
   3297 
   3298     const tracked_inst = try block.trackZir(inst);
   3299     const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
   3300 
   3301     extra_index += @intFromBool(small.has_tag_type);
   3302     const captures_len = if (small.has_captures_len) blk: {
   3303         const captures_len = sema.code.extra[extra_index];
   3304         extra_index += 1;
   3305         break :blk captures_len;
   3306     } else 0;
   3307     extra_index += @intFromBool(small.has_body_len);
   3308     const fields_len = if (small.has_fields_len) blk: {
   3309         const fields_len = sema.code.extra[extra_index];
   3310         extra_index += 1;
   3311         break :blk fields_len;
   3312     } else 0;
   3313 
   3314     const decls_len = if (small.has_decls_len) blk: {
   3315         const decls_len = sema.code.extra[extra_index];
   3316         extra_index += 1;
   3317         break :blk decls_len;
   3318     } else 0;
   3319 
   3320     const captures = try sema.getCaptures(block, src, extra_index, captures_len);
   3321     extra_index += captures_len * 2;
   3322 
   3323     const union_init: InternPool.UnionTypeInit = .{
   3324         .flags = .{
   3325             .layout = small.layout,
   3326             .status = .none,
   3327             .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
   3328                 .tagged
   3329             else if (small.layout != .auto)
   3330                 .none
   3331             else switch (block.wantSafeTypes()) {
   3332                 true => .safety,
   3333                 false => .none,
   3334             },
   3335             .any_aligned_fields = small.any_aligned_fields,
   3336             .requires_comptime = .unknown,
   3337             .assumed_runtime_bits = false,
   3338             .assumed_pointer_aligned = false,
   3339             .alignment = .none,
   3340         },
   3341         .fields_len = fields_len,
   3342         .enum_tag_ty = .none, // set later
   3343         .field_types = &.{}, // set later
   3344         .field_aligns = &.{}, // set later
   3345         .key = .{ .declared = .{
   3346             .zir_index = tracked_inst,
   3347             .captures = captures,
   3348         } },
   3349     };
   3350     const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) {
   3351         .existing => |ty| {
   3352             const new_ty = try pt.ensureTypeUpToDate(ty);
   3353 
   3354             // Make sure we update the namespace if the declaration is re-analyzed, to pick
   3355             // up on e.g. changed comptime decls.
   3356             try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(zcu));
   3357 
   3358             try sema.declareDependency(.{ .interned = new_ty });
   3359             try sema.addTypeReferenceEntry(src, new_ty);
   3360             return Air.internedToRef(new_ty);
   3361         },
   3362         .wip => |wip| wip,
   3363     };
   3364     errdefer wip_ty.cancel(ip, pt.tid);
   3365 
   3366     const type_name = try sema.createTypeName(
   3367         block,
   3368         small.name_strategy,
   3369         "union",
   3370         inst,
   3371         wip_ty.index,
   3372     );
   3373     wip_ty.setName(ip, type_name.name, type_name.nav);
   3374 
   3375     const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
   3376         .parent = block.namespace.toOptional(),
   3377         .owner_type = wip_ty.index,
   3378         .file_scope = block.getFileScopeIndex(zcu),
   3379         .generation = zcu.generation,
   3380     });
   3381     errdefer pt.destroyNamespace(new_namespace_index);
   3382 
   3383     if (pt.zcu.comp.incremental) {
   3384         try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst });
   3385     }
   3386 
   3387     const decls = sema.code.bodySlice(extra_index, decls_len);
   3388     try pt.scanNamespace(new_namespace_index, decls);
   3389 
   3390     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
   3391     codegen_type: {
   3392         if (zcu.comp.config.use_llvm) break :codegen_type;
   3393         if (block.ownerModule().strip) break :codegen_type;
   3394         // This job depends on any resolve_type_fully jobs queued up before it.
   3395         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   3396         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   3397     }
   3398     try sema.declareDependency(.{ .interned = wip_ty.index });
   3399     try sema.addTypeReferenceEntry(src, wip_ty.index);
   3400     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   3401     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
   3402 }
   3403 
   3404 fn zirOpaqueDecl(
   3405     sema: *Sema,
   3406     block: *Block,
   3407     extended: Zir.Inst.Extended.InstData,
   3408     inst: Zir.Inst.Index,
   3409 ) CompileError!Air.Inst.Ref {
   3410     const tracy = trace(@src());
   3411     defer tracy.end();
   3412 
   3413     const pt = sema.pt;
   3414     const zcu = pt.zcu;
   3415     const gpa = sema.gpa;
   3416     const ip = &zcu.intern_pool;
   3417 
   3418     const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
   3419     const extra = sema.code.extraData(Zir.Inst.OpaqueDecl, extended.operand);
   3420     var extra_index: usize = extra.end;
   3421 
   3422     const tracked_inst = try block.trackZir(inst);
   3423     const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
   3424 
   3425     const captures_len = if (small.has_captures_len) blk: {
   3426         const captures_len = sema.code.extra[extra_index];
   3427         extra_index += 1;
   3428         break :blk captures_len;
   3429     } else 0;
   3430 
   3431     const decls_len = if (small.has_decls_len) blk: {
   3432         const decls_len = sema.code.extra[extra_index];
   3433         extra_index += 1;
   3434         break :blk decls_len;
   3435     } else 0;
   3436 
   3437     const captures = try sema.getCaptures(block, src, extra_index, captures_len);
   3438     extra_index += captures_len * 2;
   3439 
   3440     const opaque_init: InternPool.OpaqueTypeInit = .{
   3441         .key = .{ .declared = .{
   3442             .zir_index = tracked_inst,
   3443             .captures = captures,
   3444         } },
   3445     };
   3446     const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) {
   3447         .existing => |ty| {
   3448             // Make sure we update the namespace if the declaration is re-analyzed, to pick
   3449             // up on e.g. changed comptime decls.
   3450             try pt.ensureNamespaceUpToDate(Type.fromInterned(ty).getNamespaceIndex(zcu));
   3451 
   3452             try sema.declareDependency(.{ .interned = ty });
   3453             try sema.addTypeReferenceEntry(src, ty);
   3454             return Air.internedToRef(ty);
   3455         },
   3456         .wip => |wip| wip,
   3457     };
   3458     errdefer wip_ty.cancel(ip, pt.tid);
   3459 
   3460     const type_name = try sema.createTypeName(
   3461         block,
   3462         small.name_strategy,
   3463         "opaque",
   3464         inst,
   3465         wip_ty.index,
   3466     );
   3467     wip_ty.setName(ip, type_name.name, type_name.nav);
   3468 
   3469     const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
   3470         .parent = block.namespace.toOptional(),
   3471         .owner_type = wip_ty.index,
   3472         .file_scope = block.getFileScopeIndex(zcu),
   3473         .generation = zcu.generation,
   3474     });
   3475     errdefer pt.destroyNamespace(new_namespace_index);
   3476 
   3477     const decls = sema.code.bodySlice(extra_index, decls_len);
   3478     try pt.scanNamespace(new_namespace_index, decls);
   3479 
   3480     codegen_type: {
   3481         if (zcu.comp.config.use_llvm) break :codegen_type;
   3482         if (block.ownerModule().strip) break :codegen_type;
   3483         // This job depends on any resolve_type_fully jobs queued up before it.
   3484         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
   3485         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
   3486     }
   3487     try sema.addTypeReferenceEntry(src, wip_ty.index);
   3488     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
   3489     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
   3490 }
   3491 
   3492 fn zirErrorSetDecl(
   3493     sema: *Sema,
   3494     inst: Zir.Inst.Index,
   3495 ) CompileError!Air.Inst.Ref {
   3496     const tracy = trace(@src());
   3497     defer tracy.end();
   3498 
   3499     const pt = sema.pt;
   3500     const zcu = pt.zcu;
   3501     const gpa = sema.gpa;
   3502     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   3503     const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index);
   3504 
   3505     var names: InferredErrorSet.NameMap = .{};
   3506     try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len);
   3507 
   3508     var extra_index: u32 = @intCast(extra.end);
   3509     const extra_index_end = extra_index + extra.data.fields_len;
   3510     while (extra_index < extra_index_end) : (extra_index += 1) {
   3511         const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]);
   3512         const name = sema.code.nullTerminatedString(name_index);
   3513         const name_ip = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
   3514         _ = try pt.getErrorValue(name_ip);
   3515         const result = names.getOrPutAssumeCapacity(name_ip);
   3516         assert(!result.found_existing); // verified in AstGen
   3517     }
   3518 
   3519     return Air.internedToRef((try pt.errorSetFromUnsortedNames(names.keys())).toIntern());
   3520 }
   3521 
   3522 fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3523     const tracy = trace(@src());
   3524     defer tracy.end();
   3525 
   3526     const pt = sema.pt;
   3527 
   3528     const src = block.nodeOffset(sema.code.instructions.items(.data)[@intFromEnum(inst)].node);
   3529 
   3530     if (block.isComptime() or try sema.fn_ret_ty.comptimeOnlySema(pt)) {
   3531         try sema.fn_ret_ty.resolveFields(pt);
   3532         return sema.analyzeComptimeAlloc(block, src, sema.fn_ret_ty, .none);
   3533     }
   3534 
   3535     const target = pt.zcu.getTarget();
   3536     const ptr_type = try pt.ptrTypeSema(.{
   3537         .child = sema.fn_ret_ty.toIntern(),
   3538         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   3539     });
   3540 
   3541     if (block.inlining != null) {
   3542         // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr.
   3543         // TODO when functions gain result location support, the inlining struct in
   3544         // Block should contain the return pointer, and we would pass that through here.
   3545         return block.addTy(.alloc, ptr_type);
   3546     }
   3547 
   3548     return block.addTy(.ret_ptr, ptr_type);
   3549 }
   3550 
   3551 fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3552     const tracy = trace(@src());
   3553     defer tracy.end();
   3554 
   3555     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
   3556     const operand = try sema.resolveInst(inst_data.operand);
   3557     return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand);
   3558 }
   3559 
   3560 fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3561     const tracy = trace(@src());
   3562     defer tracy.end();
   3563 
   3564     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3565     const operand = try sema.resolveInst(inst_data.operand);
   3566     const src = block.nodeOffset(inst_data.src_node);
   3567 
   3568     return sema.ensureResultUsed(block, sema.typeOf(operand), src);
   3569 }
   3570 
   3571 fn ensureResultUsed(
   3572     sema: *Sema,
   3573     block: *Block,
   3574     ty: Type,
   3575     src: LazySrcLoc,
   3576 ) CompileError!void {
   3577     const pt = sema.pt;
   3578     const zcu = pt.zcu;
   3579     switch (ty.zigTypeTag(zcu)) {
   3580         .void, .noreturn => return,
   3581         .error_set => return sema.fail(block, src, "error set is ignored", .{}),
   3582         .error_union => {
   3583             const msg = msg: {
   3584                 const msg = try sema.errMsg(src, "error union is ignored", .{});
   3585                 errdefer msg.destroy(sema.gpa);
   3586                 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   3587                 break :msg msg;
   3588             };
   3589             return sema.failWithOwnedErrorMsg(block, msg);
   3590         },
   3591         else => {
   3592             const msg = msg: {
   3593                 const msg = try sema.errMsg(src, "value of type '{f}' ignored", .{ty.fmt(pt)});
   3594                 errdefer msg.destroy(sema.gpa);
   3595                 try sema.errNote(src, msg, "all non-void values must be used", .{});
   3596                 try sema.errNote(src, msg, "to discard the value, assign it to '_'", .{});
   3597                 break :msg msg;
   3598             };
   3599             return sema.failWithOwnedErrorMsg(block, msg);
   3600         },
   3601     }
   3602 }
   3603 
   3604 fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3605     const tracy = trace(@src());
   3606     defer tracy.end();
   3607 
   3608     const pt = sema.pt;
   3609     const zcu = pt.zcu;
   3610     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3611     const operand = try sema.resolveInst(inst_data.operand);
   3612     const src = block.nodeOffset(inst_data.src_node);
   3613     const operand_ty = sema.typeOf(operand);
   3614     switch (operand_ty.zigTypeTag(zcu)) {
   3615         .error_set => return sema.fail(block, src, "error set is discarded", .{}),
   3616         .error_union => {
   3617             const msg = msg: {
   3618                 const msg = try sema.errMsg(src, "error union is discarded", .{});
   3619                 errdefer msg.destroy(sema.gpa);
   3620                 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   3621                 break :msg msg;
   3622             };
   3623             return sema.failWithOwnedErrorMsg(block, msg);
   3624         },
   3625         else => return,
   3626     }
   3627 }
   3628 
   3629 fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   3630     const tracy = trace(@src());
   3631     defer tracy.end();
   3632 
   3633     const pt = sema.pt;
   3634     const zcu = pt.zcu;
   3635     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3636     const src = block.nodeOffset(inst_data.src_node);
   3637     const operand = try sema.resolveInst(inst_data.operand);
   3638     const operand_ty = sema.typeOf(operand);
   3639     const err_union_ty = if (operand_ty.zigTypeTag(zcu) == .pointer)
   3640         operand_ty.childType(zcu)
   3641     else
   3642         operand_ty;
   3643     if (err_union_ty.zigTypeTag(zcu) != .error_union) return;
   3644     const payload_ty = err_union_ty.errorUnionPayload(zcu).zigTypeTag(zcu);
   3645     if (payload_ty != .void and payload_ty != .noreturn) {
   3646         const msg = msg: {
   3647             const msg = try sema.errMsg(src, "error union payload is ignored", .{});
   3648             errdefer msg.destroy(sema.gpa);
   3649             try sema.errNote(src, msg, "payload value can be explicitly ignored with '|_|'", .{});
   3650             break :msg msg;
   3651         };
   3652         return sema.failWithOwnedErrorMsg(block, msg);
   3653     }
   3654 }
   3655 
   3656 fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3657     const tracy = trace(@src());
   3658     defer tracy.end();
   3659 
   3660     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3661     const src = block.nodeOffset(inst_data.src_node);
   3662     const object = try sema.resolveInst(inst_data.operand);
   3663 
   3664     return indexablePtrLen(sema, block, src, object);
   3665 }
   3666 
   3667 fn indexablePtrLen(
   3668     sema: *Sema,
   3669     block: *Block,
   3670     src: LazySrcLoc,
   3671     object: Air.Inst.Ref,
   3672 ) CompileError!Air.Inst.Ref {
   3673     const pt = sema.pt;
   3674     const zcu = pt.zcu;
   3675     const object_ty = sema.typeOf(object);
   3676     const is_pointer_to = object_ty.isSinglePointer(zcu);
   3677     const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty;
   3678     try sema.checkIndexable(block, src, indexable_ty);
   3679     const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls);
   3680     return sema.fieldVal(block, src, object, field_name, src);
   3681 }
   3682 
   3683 fn indexablePtrLenOrNone(
   3684     sema: *Sema,
   3685     block: *Block,
   3686     src: LazySrcLoc,
   3687     operand: Air.Inst.Ref,
   3688 ) CompileError!Air.Inst.Ref {
   3689     const pt = sema.pt;
   3690     const zcu = pt.zcu;
   3691     const operand_ty = sema.typeOf(operand);
   3692     try checkMemOperand(sema, block, src, operand_ty);
   3693     switch (operand_ty.ptrSize(zcu)) {
   3694         .many, .c => return .none,
   3695         .one, .slice => {},
   3696     }
   3697     const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "len", .no_embedded_nulls);
   3698     return sema.fieldVal(block, src, operand, field_name, src);
   3699 }
   3700 
   3701 fn zirAllocExtended(
   3702     sema: *Sema,
   3703     block: *Block,
   3704     extended: Zir.Inst.Extended.InstData,
   3705 ) CompileError!Air.Inst.Ref {
   3706     const pt = sema.pt;
   3707     const gpa = sema.gpa;
   3708     const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
   3709     const ty_src = block.src(.{ .node_offset_var_decl_ty = extra.data.src_node });
   3710     const align_src = block.src(.{ .node_offset_var_decl_align = extra.data.src_node });
   3711     const init_src = block.src(.{ .node_offset_var_decl_init = extra.data.src_node });
   3712     const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small);
   3713 
   3714     var extra_index: usize = extra.end;
   3715 
   3716     const var_ty: Type = if (small.has_type) blk: {
   3717         const type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   3718         extra_index += 1;
   3719         break :blk try sema.resolveType(block, ty_src, type_ref);
   3720     } else undefined;
   3721 
   3722     const alignment = if (small.has_align) blk: {
   3723         const align_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   3724         extra_index += 1;
   3725         break :blk try sema.resolveAlign(block, align_src, align_ref);
   3726     } else .none;
   3727 
   3728     if (block.isComptime() or small.is_comptime) {
   3729         if (small.has_type) {
   3730             return sema.analyzeComptimeAlloc(block, init_src, var_ty, alignment);
   3731         } else {
   3732             try sema.air_instructions.append(gpa, .{
   3733                 .tag = .inferred_alloc_comptime,
   3734                 .data = .{ .inferred_alloc_comptime = .{
   3735                     .alignment = alignment,
   3736                     .is_const = small.is_const,
   3737                     .ptr = undefined,
   3738                 } },
   3739             });
   3740             return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
   3741         }
   3742     }
   3743 
   3744     if (small.has_type and try var_ty.comptimeOnlySema(pt)) {
   3745         return sema.analyzeComptimeAlloc(block, init_src, var_ty, alignment);
   3746     }
   3747 
   3748     if (small.has_type) {
   3749         if (!small.is_const) {
   3750             try sema.validateVarType(block, ty_src, var_ty, false);
   3751         }
   3752         const target = pt.zcu.getTarget();
   3753         try var_ty.resolveLayout(pt);
   3754         if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
   3755             const var_src = block.src(.{ .node_offset_store_ptr = extra.data.src_node });
   3756             return sema.fail(block, var_src, "local variable in naked function", .{});
   3757         }
   3758         const ptr_type = try sema.pt.ptrTypeSema(.{
   3759             .child = var_ty.toIntern(),
   3760             .flags = .{
   3761                 .alignment = alignment,
   3762                 .address_space = target_util.defaultAddressSpace(target, .local),
   3763             },
   3764         });
   3765         const ptr = try block.addTy(.alloc, ptr_type);
   3766         if (small.is_const) {
   3767             const ptr_inst = ptr.toIndex().?;
   3768             try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index });
   3769             try sema.base_allocs.put(gpa, ptr_inst, ptr_inst);
   3770         }
   3771         return ptr;
   3772     }
   3773 
   3774     const result_index = try block.addInstAsIndex(.{
   3775         .tag = .inferred_alloc,
   3776         .data = .{ .inferred_alloc = .{
   3777             .alignment = alignment,
   3778             .is_const = small.is_const,
   3779         } },
   3780     });
   3781     try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
   3782     if (small.is_const) {
   3783         try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index });
   3784         try sema.base_allocs.put(gpa, result_index, result_index);
   3785     }
   3786     return result_index.toRef();
   3787 }
   3788 
   3789 fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3790     const tracy = trace(@src());
   3791     defer tracy.end();
   3792 
   3793     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3794     const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
   3795     const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node });
   3796     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   3797     return sema.analyzeComptimeAlloc(block, init_src, var_ty, .none);
   3798 }
   3799 
   3800 fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   3801     const pt = sema.pt;
   3802     const zcu = pt.zcu;
   3803     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   3804     const alloc = try sema.resolveInst(inst_data.operand);
   3805     const alloc_ty = sema.typeOf(alloc);
   3806     const ptr_info = alloc_ty.ptrInfo(zcu);
   3807     const elem_ty: Type = .fromInterned(ptr_info.child);
   3808 
   3809     // If the alloc was created in a comptime scope, we already created a comptime alloc for it.
   3810     // However, if the final constructed value does not reference comptime-mutable memory, we wish
   3811     // to promote it to an anon decl.
   3812     already_ct: {
   3813         const ptr_val = try sema.resolveValue(alloc) orelse break :already_ct;
   3814 
   3815         // If this was a comptime inferred alloc, then `storeToInferredAllocComptime`
   3816         // might have already done our job and created an anon decl ref.
   3817         switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
   3818             .ptr => |ptr| switch (ptr.base_addr) {
   3819                 .uav => {
   3820                     // The comptime-ification was already done for us.
   3821                     // Just make sure the pointer is const.
   3822                     return sema.makePtrConst(block, alloc);
   3823                 },
   3824                 else => {},
   3825             },
   3826             else => {},
   3827         }
   3828 
   3829         if (!sema.isComptimeMutablePtr(ptr_val)) break :already_ct;
   3830         const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
   3831         assert(ptr.byte_offset == 0);
   3832         const alloc_index = ptr.base_addr.comptime_alloc;
   3833         const ct_alloc = sema.getComptimeAlloc(alloc_index);
   3834         const interned = try ct_alloc.val.intern(pt, sema.arena);
   3835         if (interned.canMutateComptimeVarState(zcu)) {
   3836             // Preserve the comptime alloc, just make the pointer const.
   3837             ct_alloc.val = .{ .interned = interned.toIntern() };
   3838             ct_alloc.is_const = true;
   3839             return sema.makePtrConst(block, alloc);
   3840         } else {
   3841             // Promote the constant to an anon decl.
   3842             const new_mut_ptr = Air.internedToRef(try pt.intern(.{ .ptr = .{
   3843                 .ty = alloc_ty.toIntern(),
   3844                 .base_addr = .{ .uav = .{
   3845                     .val = interned.toIntern(),
   3846                     .orig_ty = alloc_ty.toIntern(),
   3847                 } },
   3848                 .byte_offset = 0,
   3849             } }));
   3850             return sema.makePtrConst(block, new_mut_ptr);
   3851         }
   3852     }
   3853 
   3854     // Otherwise, check if the alloc is comptime-known despite being in a runtime scope.
   3855     if (try sema.resolveComptimeKnownAllocPtr(block, alloc, null)) |ptr_val| {
   3856         return sema.makePtrConst(block, Air.internedToRef(ptr_val));
   3857     }
   3858 
   3859     if (try elem_ty.comptimeOnlySema(pt)) {
   3860         // The value was initialized through RLS, so we didn't detect the runtime condition earlier.
   3861         // TODO: source location of runtime control flow
   3862         const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node });
   3863         return sema.fail(block, init_src, "value with comptime-only type '{f}' depends on runtime control flow", .{elem_ty.fmt(pt)});
   3864     }
   3865 
   3866     // This is a runtime value.
   3867     return sema.makePtrConst(block, alloc);
   3868 }
   3869 
   3870 /// If `alloc` is an inferred allocation, `resolved_inferred_ty` is taken to be its resolved
   3871 /// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`.
   3872 fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index {
   3873     const pt = sema.pt;
   3874     const zcu = pt.zcu;
   3875 
   3876     const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc);
   3877     const ptr_info = alloc_ty.ptrInfo(zcu);
   3878     const elem_ty: Type = .fromInterned(ptr_info.child);
   3879 
   3880     const alloc_inst = alloc.toIndex() orelse return null;
   3881     const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null;
   3882     const stores = comptime_info.value.stores.items(.inst);
   3883 
   3884     // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known.
   3885     // We will resolve and return its value.
   3886 
   3887     // We expect to have emitted at least one store, unless the elem type is OPV.
   3888     if (stores.len == 0) {
   3889         const val = (try sema.typeHasOnePossibleValue(elem_ty)).?.toIntern();
   3890         return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value);
   3891     }
   3892 
   3893     // In general, we want to create a comptime alloc of the correct type and
   3894     // apply the stores to that alloc in order. However, before going to all
   3895     // that effort, let's optimize for the common case of a single store.
   3896 
   3897     simple: {
   3898         if (stores.len != 1) break :simple;
   3899         const store_inst = sema.air_instructions.get(@intFromEnum(stores[0]));
   3900         switch (store_inst.tag) {
   3901             .store, .store_safe => {},
   3902             .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => break :simple, // there's OPV stuff going on!
   3903             else => unreachable,
   3904         }
   3905         if (store_inst.data.bin_op.lhs != alloc) break :simple;
   3906 
   3907         const val = store_inst.data.bin_op.rhs.toInterned().?;
   3908         assert(zcu.intern_pool.typeOf(val) == elem_ty.toIntern());
   3909         return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value);
   3910     }
   3911 
   3912     // The simple strategy failed: we must create a mutable comptime alloc and
   3913     // perform all of the runtime store operations at comptime.
   3914 
   3915     const ct_alloc = try sema.newComptimeAlloc(block, .unneeded, elem_ty, ptr_info.flags.alignment);
   3916 
   3917     const alloc_ptr = try pt.intern(.{ .ptr = .{
   3918         .ty = alloc_ty.toIntern(),
   3919         .base_addr = .{ .comptime_alloc = ct_alloc },
   3920         .byte_offset = 0,
   3921     } });
   3922 
   3923     // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the comptime alloc
   3924     var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena);
   3925     try ptr_mapping.ensureTotalCapacity(@intCast(stores.len));
   3926     ptr_mapping.putAssumeCapacity(alloc_inst, alloc_ptr);
   3927 
   3928     // Whilst constructing our mapping, we will also initialize optional and error union payloads when
   3929     // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters.
   3930     var to_map = try std.ArrayList(Air.Inst.Index).initCapacity(sema.arena, stores.len);
   3931     for (stores) |store_inst_idx| {
   3932         const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
   3933         const ptr_to_map = switch (store_inst.tag) {
   3934             .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to.
   3935             .set_union_tag => continue, // Ignore for now; handled after we map pointers
   3936             .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.
   3937             else => unreachable,
   3938         };
   3939         to_map.appendAssumeCapacity(ptr_to_map);
   3940     }
   3941 
   3942     const tmp_air = sema.getTmpAir();
   3943 
   3944     while (to_map.pop()) |air_ptr| {
   3945         if (ptr_mapping.contains(air_ptr)) continue;
   3946         const PointerMethod = union(enum) {
   3947             same_addr,
   3948             opt_payload,
   3949             eu_payload,
   3950             field: u32,
   3951             elem: u64,
   3952         };
   3953         const inst_tag = tmp_air.instructions.items(.tag)[@intFromEnum(air_ptr)];
   3954         const air_parent_ptr: Air.Inst.Ref, const method: PointerMethod = switch (inst_tag) {
   3955             .struct_field_ptr => blk: {
   3956                 const data = tmp_air.extraData(
   3957                     Air.StructField,
   3958                     tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload,
   3959                 ).data;
   3960                 break :blk .{
   3961                     data.struct_operand,
   3962                     .{ .field = data.field_index },
   3963                 };
   3964             },
   3965             .struct_field_ptr_index_0,
   3966             .struct_field_ptr_index_1,
   3967             .struct_field_ptr_index_2,
   3968             .struct_field_ptr_index_3,
   3969             => .{
   3970                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   3971                 .{ .field = switch (inst_tag) {
   3972                     .struct_field_ptr_index_0 => 0,
   3973                     .struct_field_ptr_index_1 => 1,
   3974                     .struct_field_ptr_index_2 => 2,
   3975                     .struct_field_ptr_index_3 => 3,
   3976                     else => unreachable,
   3977                 } },
   3978             },
   3979             .ptr_slice_ptr_ptr => .{
   3980                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   3981                 .{ .field = Value.slice_ptr_index },
   3982             },
   3983             .ptr_slice_len_ptr => .{
   3984                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   3985                 .{ .field = Value.slice_len_index },
   3986             },
   3987             .ptr_elem_ptr => blk: {
   3988                 const data = tmp_air.extraData(
   3989                     Air.Bin,
   3990                     tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_pl.payload,
   3991                 ).data;
   3992                 const idx_val = (try sema.resolveValue(data.rhs)).?;
   3993                 break :blk .{
   3994                     data.lhs,
   3995                     .{ .elem = try idx_val.toUnsignedIntSema(pt) },
   3996                 };
   3997             },
   3998             .bitcast => .{
   3999                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   4000                 .same_addr,
   4001             },
   4002             .optional_payload_ptr_set => .{
   4003                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   4004                 .opt_payload,
   4005             },
   4006             .errunion_payload_ptr_set => .{
   4007                 tmp_air.instructions.items(.data)[@intFromEnum(air_ptr)].ty_op.operand,
   4008                 .eu_payload,
   4009             },
   4010             else => unreachable,
   4011         };
   4012 
   4013         const decl_parent_ptr = ptr_mapping.get(air_parent_ptr.toIndex().?) orelse {
   4014             // Resolve the parent pointer first.
   4015             // Note that we add in what seems like the wrong order, because we're popping from the end of this array.
   4016             try to_map.appendSlice(&.{ air_ptr, air_parent_ptr.toIndex().? });
   4017             continue;
   4018         };
   4019         const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &zcu.intern_pool).toIntern();
   4020         const new_ptr = switch (method) {
   4021             .same_addr => try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, decl_parent_ptr, new_ptr_ty),
   4022             .opt_payload => ptr: {
   4023                 // Set the optional to non-null at comptime.
   4024                 // If the payload is OPV, we must use that value instead of undef.
   4025                 const opt_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
   4026                 const payload_ty = opt_ty.optionalChild(zcu);
   4027                 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
   4028                 const opt_val = try pt.intern(.{ .opt = .{
   4029                     .ty = opt_ty.toIntern(),
   4030                     .val = payload_val.toIntern(),
   4031                 } });
   4032                 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(opt_val), opt_ty);
   4033                 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrOptPayload(pt)).toIntern();
   4034             },
   4035             .eu_payload => ptr: {
   4036                 // Set the error union to non-error at comptime.
   4037                 // If the payload is OPV, we must use that value instead of undef.
   4038                 const eu_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
   4039                 const payload_ty = eu_ty.errorUnionPayload(zcu);
   4040                 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
   4041                 const eu_val = try pt.intern(.{ .error_union = .{
   4042                     .ty = eu_ty.toIntern(),
   4043                     .val = .{ .payload = payload_val.toIntern() },
   4044                 } });
   4045                 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(eu_val), eu_ty);
   4046                 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrEuPayload(pt)).toIntern();
   4047             },
   4048             .field => |idx| ptr: {
   4049                 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
   4050                 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| {
   4051                     // As this is a union field, we must store to the pointer now to set the tag.
   4052                     // If the payload is OPV, there will not be a payload store, so we store that value.
   4053                     // Otherwise, there will be a payload store to process later, so undef will suffice.
   4054                     const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]);
   4055                     const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
   4056                     const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), idx);
   4057                     const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);
   4058                     try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), store_val, maybe_union_ty);
   4059                 }
   4060                 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
   4061             },
   4062             .elem => |idx| (try Value.fromInterned(decl_parent_ptr).ptrElem(idx, pt)).toIntern(),
   4063         };
   4064         try ptr_mapping.put(air_ptr, new_ptr);
   4065     }
   4066 
   4067     // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime.
   4068     // Any implicit stores performed by `optional_payload_ptr_set` or `errunion_payload_ptr_set`
   4069     // instructions were already done above.
   4070 
   4071     for (stores) |store_inst_idx| {
   4072         const store_inst = sema.air_instructions.get(@intFromEnum(store_inst_idx));
   4073         switch (store_inst.tag) {
   4074             .optional_payload_ptr_set, .errunion_payload_ptr_set => {}, // Handled explicitly above
   4075             .set_union_tag => {
   4076                 // Usually, we can ignore these, because the creation of the field pointer above
   4077                 // already did it for us. However, if the field is OPV, this is relevant, because
   4078                 // there is not going to be a store to the field. So we must initialize the union
   4079                 // tag if the field is OPV.
   4080                 const union_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
   4081                 const union_ptr_val: Value = .fromInterned(ptr_mapping.get(union_ptr_inst).?);
   4082                 const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?);
   4083                 const union_ty = union_ptr_val.typeOf(zcu).childType(zcu);
   4084                 const field_ty = union_ty.unionFieldType(tag_val, zcu).?;
   4085                 if (try sema.typeHasOnePossibleValue(field_ty)) |payload_val| {
   4086                     const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val);
   4087                     try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty);
   4088                 }
   4089             },
   4090             .store, .store_safe => {
   4091                 const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
   4092                 const store_val = (try sema.resolveValue(store_inst.data.bin_op.rhs)).?;
   4093                 const new_ptr = ptr_mapping.get(air_ptr_inst).?;
   4094                 try sema.storePtrVal(block, .unneeded, .fromInterned(new_ptr), store_val, store_val.typeOf(zcu));
   4095             },
   4096             else => unreachable,
   4097         }
   4098     }
   4099 
   4100     // The value is finalized - load it!
   4101     const val = (try sema.pointerDeref(block, LazySrcLoc.unneeded, Value.fromInterned(alloc_ptr), alloc_ty)).?.toIntern();
   4102     return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, ct_alloc, alloc_inst, comptime_info.value);
   4103 }
   4104 
   4105 /// Given the resolved comptime-known value, rewrites the dead AIR to not
   4106 /// create a runtime stack allocation. Also places the resulting value into
   4107 /// either an anon decl ref or a comptime alloc depending on whether it
   4108 /// references comptime-mutable memory. If `existing_comptime_alloc` is
   4109 /// passed, it is a scratch allocation which already contains `result_val`.
   4110 /// Same return type as `resolveComptimeKnownAllocPtr` so we can tail call.
   4111 fn finishResolveComptimeKnownAllocPtr(
   4112     sema: *Sema,
   4113     block: *Block,
   4114     alloc_ty: Type,
   4115     result_val: InternPool.Index,
   4116     existing_comptime_alloc: ?ComptimeAllocIndex,
   4117     alloc_inst: Air.Inst.Index,
   4118     comptime_info: MaybeComptimeAlloc,
   4119 ) CompileError!?InternPool.Index {
   4120     const pt = sema.pt;
   4121     const zcu = pt.zcu;
   4122 
   4123     // We're almost done - we have the resolved comptime value. We just need to
   4124     // eliminate the now-dead runtime instructions.
   4125 
   4126     // This instruction has type `alloc_ty`, meaning we can rewrite the `alloc` AIR instruction to
   4127     // this one to drop the side effect. We also need to rewrite the stores; we'll turn them to this
   4128     // too because it doesn't really matter what they become.
   4129     const nop_inst: Air.Inst = .{ .tag = .bitcast, .data = .{ .ty_op = .{
   4130         .ty = .fromIntern(alloc_ty.toIntern()),
   4131         .operand = .zero_usize,
   4132     } } };
   4133 
   4134     sema.air_instructions.set(@intFromEnum(alloc_inst), nop_inst);
   4135     for (comptime_info.stores.items(.inst)) |store_inst| {
   4136         sema.air_instructions.set(@intFromEnum(store_inst), nop_inst);
   4137     }
   4138 
   4139     if (Value.fromInterned(result_val).canMutateComptimeVarState(zcu)) {
   4140         const alloc_index = existing_comptime_alloc orelse a: {
   4141             const idx = try sema.newComptimeAlloc(block, .unneeded, alloc_ty.childType(zcu), alloc_ty.ptrAlignment(zcu));
   4142             const alloc = sema.getComptimeAlloc(idx);
   4143             alloc.val = .{ .interned = result_val };
   4144             break :a idx;
   4145         };
   4146         sema.getComptimeAlloc(alloc_index).is_const = true;
   4147         return try pt.intern(.{ .ptr = .{
   4148             .ty = alloc_ty.toIntern(),
   4149             .base_addr = .{ .comptime_alloc = alloc_index },
   4150             .byte_offset = 0,
   4151         } });
   4152     } else {
   4153         return try pt.intern(.{ .ptr = .{
   4154             .ty = alloc_ty.toIntern(),
   4155             .base_addr = .{ .uav = .{
   4156                 .orig_ty = alloc_ty.toIntern(),
   4157                 .val = result_val,
   4158             } },
   4159             .byte_offset = 0,
   4160         } });
   4161     }
   4162 }
   4163 
   4164 fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type {
   4165     var ptr_info = ptr_ty.ptrInfo(sema.pt.zcu);
   4166     ptr_info.flags.is_const = true;
   4167     return sema.pt.ptrTypeSema(ptr_info);
   4168 }
   4169 
   4170 fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   4171     const alloc_ty = sema.typeOf(alloc);
   4172     const const_ptr_ty = try sema.makePtrTyConst(alloc_ty);
   4173 
   4174     // Detect if a comptime value simply needs to have its type changed.
   4175     if (try sema.resolveValue(alloc)) |val| {
   4176         return Air.internedToRef((try sema.pt.getCoerced(val, const_ptr_ty)).toIntern());
   4177     }
   4178 
   4179     return block.addBitCast(const_ptr_ty, alloc);
   4180 }
   4181 
   4182 fn zirAllocInferredComptime(
   4183     sema: *Sema,
   4184     is_const: bool,
   4185 ) CompileError!Air.Inst.Ref {
   4186     const gpa = sema.gpa;
   4187 
   4188     try sema.air_instructions.append(gpa, .{
   4189         .tag = .inferred_alloc_comptime,
   4190         .data = .{ .inferred_alloc_comptime = .{
   4191             .alignment = .none,
   4192             .is_const = is_const,
   4193             .ptr = undefined,
   4194         } },
   4195     });
   4196     return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
   4197 }
   4198 
   4199 fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4200     const tracy = trace(@src());
   4201     defer tracy.end();
   4202 
   4203     const pt = sema.pt;
   4204 
   4205     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4206     const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
   4207     const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node });
   4208 
   4209     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   4210     if (block.isComptime() or try var_ty.comptimeOnlySema(pt)) {
   4211         return sema.analyzeComptimeAlloc(block, init_src, var_ty, .none);
   4212     }
   4213     if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
   4214         const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
   4215         return sema.fail(block, mut_src, "local variable in naked function", .{});
   4216     }
   4217     const target = pt.zcu.getTarget();
   4218     const ptr_type = try pt.ptrTypeSema(.{
   4219         .child = var_ty.toIntern(),
   4220         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   4221     });
   4222     const ptr = try block.addTy(.alloc, ptr_type);
   4223     const ptr_inst = ptr.toIndex().?;
   4224     try sema.maybe_comptime_allocs.put(sema.gpa, ptr_inst, .{ .runtime_index = block.runtime_index });
   4225     try sema.base_allocs.put(sema.gpa, ptr_inst, ptr_inst);
   4226     return ptr;
   4227 }
   4228 
   4229 fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4230     const tracy = trace(@src());
   4231     defer tracy.end();
   4232 
   4233     const pt = sema.pt;
   4234 
   4235     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4236     const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
   4237     const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node });
   4238     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
   4239     if (block.isComptime()) {
   4240         return sema.analyzeComptimeAlloc(block, init_src, var_ty, .none);
   4241     }
   4242     if (sema.func_is_naked and try var_ty.hasRuntimeBitsSema(pt)) {
   4243         const var_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
   4244         return sema.fail(block, var_src, "local variable in naked function", .{});
   4245     }
   4246     try sema.validateVarType(block, ty_src, var_ty, false);
   4247     const target = pt.zcu.getTarget();
   4248     const ptr_type = try pt.ptrTypeSema(.{
   4249         .child = var_ty.toIntern(),
   4250         .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
   4251     });
   4252     return block.addTy(.alloc, ptr_type);
   4253 }
   4254 
   4255 fn zirAllocInferred(
   4256     sema: *Sema,
   4257     block: *Block,
   4258     is_const: bool,
   4259 ) CompileError!Air.Inst.Ref {
   4260     const tracy = trace(@src());
   4261     defer tracy.end();
   4262 
   4263     const gpa = sema.gpa;
   4264 
   4265     if (block.isComptime()) {
   4266         try sema.air_instructions.append(gpa, .{
   4267             .tag = .inferred_alloc_comptime,
   4268             .data = .{ .inferred_alloc_comptime = .{
   4269                 .alignment = .none,
   4270                 .is_const = is_const,
   4271                 .ptr = undefined,
   4272             } },
   4273         });
   4274         return @as(Air.Inst.Index, @enumFromInt(sema.air_instructions.len - 1)).toRef();
   4275     }
   4276 
   4277     const result_index = try block.addInstAsIndex(.{
   4278         .tag = .inferred_alloc,
   4279         .data = .{ .inferred_alloc = .{
   4280             .alignment = .none,
   4281             .is_const = is_const,
   4282         } },
   4283     });
   4284     try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
   4285     if (is_const) {
   4286         try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index });
   4287         try sema.base_allocs.put(sema.gpa, result_index, result_index);
   4288     }
   4289     return result_index.toRef();
   4290 }
   4291 
   4292 fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4293     const tracy = trace(@src());
   4294     defer tracy.end();
   4295 
   4296     const pt = sema.pt;
   4297     const zcu = pt.zcu;
   4298     const gpa = sema.gpa;
   4299     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4300     const src = block.nodeOffset(inst_data.src_node);
   4301     const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
   4302     const ptr = try sema.resolveInst(inst_data.operand);
   4303     const ptr_inst = ptr.toIndex().?;
   4304     const target = zcu.getTarget();
   4305 
   4306     switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) {
   4307         .inferred_alloc_comptime => {
   4308             // The work was already done for us by `Sema.storeToInferredAllocComptime`.
   4309             // All we need to do is return the pointer.
   4310             const iac = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc_comptime;
   4311             const resolved_ptr = iac.ptr;
   4312 
   4313             if (std.debug.runtime_safety) {
   4314                 // The inferred_alloc_comptime should never be referenced again
   4315                 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ .tag = undefined, .data = undefined });
   4316             }
   4317 
   4318             const val = switch (zcu.intern_pool.indexToKey(resolved_ptr).ptr.base_addr) {
   4319                 .uav => |a| a.val,
   4320                 .comptime_alloc => |i| val: {
   4321                     const alloc = sema.getComptimeAlloc(i);
   4322                     break :val (try alloc.val.intern(pt, sema.arena)).toIntern();
   4323                 },
   4324                 else => unreachable,
   4325             };
   4326             if (zcu.intern_pool.isFuncBody(val)) {
   4327                 const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));
   4328                 if (try ty.fnHasRuntimeBitsSema(pt)) {
   4329                     try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = val }));
   4330                     try zcu.ensureFuncBodyAnalysisQueued(val);
   4331                 }
   4332             }
   4333 
   4334             return Air.internedToRef(resolved_ptr);
   4335         },
   4336         .inferred_alloc => {
   4337             const ia1 = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].inferred_alloc;
   4338             const ia2 = sema.unresolved_inferred_allocs.fetchSwapRemove(ptr_inst).?.value;
   4339             const peer_vals = try sema.arena.alloc(Air.Inst.Ref, ia2.prongs.items.len);
   4340             for (peer_vals, ia2.prongs.items) |*peer_val, store_inst| {
   4341                 assert(sema.air_instructions.items(.tag)[@intFromEnum(store_inst)] == .store);
   4342                 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
   4343                 peer_val.* = bin_op.rhs;
   4344             }
   4345             const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none);
   4346 
   4347             const final_ptr_ty = try pt.ptrTypeSema(.{
   4348                 .child = final_elem_ty.toIntern(),
   4349                 .flags = .{
   4350                     .alignment = ia1.alignment,
   4351                     .address_space = target_util.defaultAddressSpace(target, .local),
   4352                 },
   4353             });
   4354 
   4355             if (!ia1.is_const) {
   4356                 try sema.validateVarType(block, ty_src, final_elem_ty, false);
   4357             } else if (try sema.resolveComptimeKnownAllocPtr(block, ptr, final_ptr_ty)) |ptr_val| {
   4358                 const const_ptr_ty = try sema.makePtrTyConst(final_ptr_ty);
   4359                 const new_const_ptr = try pt.getCoerced(Value.fromInterned(ptr_val), const_ptr_ty);
   4360 
   4361                 // Unless the block is comptime, `alloc_inferred` always produces
   4362                 // a runtime constant. The final inferred type needs to be
   4363                 // fully resolved so it can be lowered in codegen.
   4364                 try final_elem_ty.resolveFully(pt);
   4365 
   4366                 return Air.internedToRef(new_const_ptr.toIntern());
   4367             }
   4368 
   4369             if (try final_elem_ty.comptimeOnlySema(pt)) {
   4370                 // The alloc wasn't comptime-known per the above logic, so the
   4371                 // type cannot be comptime-only.
   4372                 // TODO: source location of runtime control flow
   4373                 return sema.fail(block, src, "value with comptime-only type '{f}' depends on runtime control flow", .{final_elem_ty.fmt(pt)});
   4374             }
   4375             if (sema.func_is_naked and try final_elem_ty.hasRuntimeBitsSema(pt)) {
   4376                 const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
   4377                 return sema.fail(block, mut_src, "local variable in naked function", .{});
   4378             }
   4379             // Change it to a normal alloc.
   4380             sema.air_instructions.set(@intFromEnum(ptr_inst), .{
   4381                 .tag = .alloc,
   4382                 .data = .{ .ty = final_ptr_ty },
   4383             });
   4384 
   4385             // Now we need to go back over all the store instructions, and do the logic as if
   4386             // the new result ptr type was available.
   4387 
   4388             for (ia2.prongs.items) |placeholder_inst| {
   4389                 var replacement_block = block.makeSubBlock();
   4390                 defer replacement_block.instructions.deinit(gpa);
   4391 
   4392                 assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .store);
   4393                 const bin_op = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].bin_op;
   4394                 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .store);
   4395 
   4396                 // If only one instruction is produced then we can replace the store
   4397                 // placeholder instruction with this instruction; no need for an entire block.
   4398                 if (replacement_block.instructions.items.len == 1) {
   4399                     const only_inst = replacement_block.instructions.items[0];
   4400                     sema.air_instructions.set(@intFromEnum(placeholder_inst), sema.air_instructions.get(@intFromEnum(only_inst)));
   4401                     continue;
   4402                 }
   4403 
   4404                 // Here we replace the placeholder store instruction with a block
   4405                 // that does the actual store logic.
   4406                 _ = try replacement_block.addBr(placeholder_inst, .void_value);
   4407                 try sema.air_extra.ensureUnusedCapacity(
   4408                     gpa,
   4409                     @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len,
   4410                 );
   4411                 sema.air_instructions.set(@intFromEnum(placeholder_inst), .{
   4412                     .tag = .block,
   4413                     .data = .{ .ty_pl = .{
   4414                         .ty = .void_type,
   4415                         .payload = sema.addExtraAssumeCapacity(Air.Block{
   4416                             .body_len = @intCast(replacement_block.instructions.items.len),
   4417                         }),
   4418                     } },
   4419                 });
   4420                 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
   4421             }
   4422 
   4423             if (ia1.is_const) {
   4424                 return sema.makePtrConst(block, ptr);
   4425             } else {
   4426                 return ptr;
   4427             }
   4428         },
   4429         else => unreachable,
   4430     }
   4431 }
   4432 
   4433 fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4434     const pt = sema.pt;
   4435     const zcu = pt.zcu;
   4436     const gpa = sema.gpa;
   4437     const ip = &zcu.intern_pool;
   4438     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4439     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
   4440     const all_args = sema.code.refSlice(extra.end, extra.data.operands_len);
   4441     const arg_pairs: []const [2]Zir.Inst.Ref = @as([*]const [2]Zir.Inst.Ref, @ptrCast(all_args))[0..@divExact(all_args.len, 2)];
   4442     const src = block.nodeOffset(inst_data.src_node);
   4443 
   4444     var len: Air.Inst.Ref = .none;
   4445     var len_val: ?Value = null;
   4446     var len_idx: u32 = undefined;
   4447     var any_runtime = false;
   4448 
   4449     const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, arg_pairs.len);
   4450     defer gpa.free(runtime_arg_lens);
   4451 
   4452     // First pass to look for comptime values.
   4453     for (arg_pairs, 0..) |zir_arg_pair, i_usize| {
   4454         const i: u32 = @intCast(i_usize);
   4455         runtime_arg_lens[i] = .none;
   4456         if (zir_arg_pair[0] == .none) continue;
   4457 
   4458         const arg_src = block.src(.{ .for_input = .{
   4459             .for_node_offset = inst_data.src_node,
   4460             .input_index = i,
   4461         } });
   4462 
   4463         const arg_len_uncoerced = if (zir_arg_pair[1] == .none) l: {
   4464             // This argument is an indexable.
   4465             const object = try sema.resolveInst(zir_arg_pair[0]);
   4466             const object_ty = sema.typeOf(object);
   4467             if (!object_ty.isIndexable(zcu)) {
   4468                 // Instead of using checkIndexable we customize this error.
   4469                 const msg = msg: {
   4470                     const msg = try sema.errMsg(arg_src, "type '{f}' is not indexable and not a range", .{object_ty.fmt(pt)});
   4471                     errdefer msg.destroy(sema.gpa);
   4472                     try sema.errNote(arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{});
   4473 
   4474                     if (object_ty.zigTypeTag(zcu) == .error_union) {
   4475                         try sema.errNote(arg_src, msg, "consider using 'try', 'catch', or 'if'", .{});
   4476                     }
   4477 
   4478                     break :msg msg;
   4479                 };
   4480                 return sema.failWithOwnedErrorMsg(block, msg);
   4481             }
   4482             if (!object_ty.indexableHasLen(zcu)) continue;
   4483             break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), arg_src);
   4484         } else l: {
   4485             // This argument is a range.
   4486             const range_start = try sema.resolveInst(zir_arg_pair[0]);
   4487             const range_end = try sema.resolveInst(zir_arg_pair[1]);
   4488             break :l try sema.analyzeArithmetic(block, .sub, range_end, range_start, arg_src, arg_src, arg_src, true);
   4489         };
   4490         const arg_len = try sema.coerce(block, .usize, arg_len_uncoerced, arg_src);
   4491         if (len == .none) {
   4492             len = arg_len;
   4493             len_idx = i;
   4494         }
   4495         if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| {
   4496             if (len_val) |v| {
   4497                 if (!(try sema.valuesEqual(arg_val, v, .usize))) {
   4498                     const msg = msg: {
   4499                         const msg = try sema.errMsg(src, "non-matching for loop lengths", .{});
   4500                         errdefer msg.destroy(gpa);
   4501                         const a_src = block.src(.{ .for_input = .{
   4502                             .for_node_offset = inst_data.src_node,
   4503                             .input_index = len_idx,
   4504                         } });
   4505                         try sema.errNote(a_src, msg, "length {f} here", .{
   4506                             v.fmtValueSema(pt, sema),
   4507                         });
   4508                         try sema.errNote(arg_src, msg, "length {f} here", .{
   4509                             arg_val.fmtValueSema(pt, sema),
   4510                         });
   4511                         break :msg msg;
   4512                     };
   4513                     return sema.failWithOwnedErrorMsg(block, msg);
   4514                 }
   4515             } else {
   4516                 len = arg_len;
   4517                 len_val = arg_val;
   4518                 len_idx = i;
   4519             }
   4520             continue;
   4521         }
   4522         runtime_arg_lens[i] = arg_len;
   4523         any_runtime = true;
   4524     }
   4525 
   4526     if (len == .none) {
   4527         const msg = msg: {
   4528             const msg = try sema.errMsg(src, "unbounded for loop", .{});
   4529             errdefer msg.destroy(gpa);
   4530             for (arg_pairs, 0..) |zir_arg_pair, i_usize| {
   4531                 const i: u32 = @intCast(i_usize);
   4532                 if (zir_arg_pair[0] == .none) continue;
   4533                 if (zir_arg_pair[1] != .none) continue;
   4534                 const object = try sema.resolveInst(zir_arg_pair[0]);
   4535                 const object_ty = sema.typeOf(object);
   4536                 const arg_src = block.src(.{ .for_input = .{
   4537                     .for_node_offset = inst_data.src_node,
   4538                     .input_index = i,
   4539                 } });
   4540                 try sema.errNote(arg_src, msg, "type '{f}' has no upper bound", .{
   4541                     object_ty.fmt(pt),
   4542                 });
   4543             }
   4544             break :msg msg;
   4545         };
   4546         return sema.failWithOwnedErrorMsg(block, msg);
   4547     }
   4548 
   4549     // Now for the runtime checks.
   4550     if (any_runtime and block.wantSafety()) {
   4551         for (runtime_arg_lens, 0..) |arg_len, i| {
   4552             if (arg_len == .none) continue;
   4553             if (i == len_idx) continue;
   4554             const ok = try block.addBinOp(.cmp_eq, len, arg_len);
   4555             try sema.addSafetyCheck(block, src, ok, .for_len_mismatch);
   4556         }
   4557     }
   4558 
   4559     return len;
   4560 }
   4561 
   4562 /// Given any single pointer, retrieve a pointer to the payload of any optional
   4563 /// or error union pointed to, initializing these pointers along the way.
   4564 /// Given a `*E!?T`, returns a (valid) `*T`.
   4565 /// May invalidate already-stored payload data.
   4566 fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref {
   4567     const pt = sema.pt;
   4568     const zcu = pt.zcu;
   4569     var base_ptr = ptr;
   4570     while (true) switch (sema.typeOf(base_ptr).childType(zcu).zigTypeTag(zcu)) {
   4571         .error_union => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
   4572         .optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
   4573         else => break,
   4574     };
   4575     try sema.checkKnownAllocPtr(block, ptr, base_ptr);
   4576     return base_ptr;
   4577 }
   4578 
   4579 fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4580     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4581     const ptr = try sema.resolveInst(un_node.operand);
   4582     return sema.optEuBasePtrInit(block, ptr, block.nodeOffset(un_node.src_node));
   4583 }
   4584 
   4585 fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   4586     const pt = sema.pt;
   4587     const zcu = pt.zcu;
   4588     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4589     const src = block.nodeOffset(pl_node.src_node);
   4590     const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data;
   4591     const uncoerced_val = try sema.resolveInst(extra.rhs);
   4592     const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.lhs) orelse return uncoerced_val;
   4593     const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu);
   4594     assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
   4595     const elem_ty = ptr_ty.childType(zcu);
   4596     switch (ptr_ty.ptrSize(zcu)) {
   4597         .one => {
   4598             const uncoerced_ty = sema.typeOf(uncoerced_val);
   4599             if (elem_ty.zigTypeTag(zcu) == .array and elem_ty.childType(zcu).toIntern() == uncoerced_ty.toIntern()) {
   4600                 // We're trying to initialize a *[1]T with a reference to a T - don't perform any coercion.
   4601                 return uncoerced_val;
   4602             }
   4603             // If the destination type is anyopaque, don't coerce - the pointer will coerce instead.
   4604             if (elem_ty.toIntern() == .anyopaque_type) {
   4605                 return uncoerced_val;
   4606             } else {
   4607                 return sema.coerce(block, elem_ty, uncoerced_val, src);
   4608             }
   4609         },
   4610         .slice, .many => {
   4611             // Our goal is to coerce `uncoerced_val` to an array of `elem_ty`.
   4612             const val_ty = sema.typeOf(uncoerced_val);
   4613             switch (val_ty.zigTypeTag(zcu)) {
   4614                 .array, .vector => {},
   4615                 else => if (!val_ty.isTuple(zcu)) {
   4616                     return sema.fail(block, src, "expected array of '{f}', found '{f}'", .{ elem_ty.fmt(pt), val_ty.fmt(pt) });
   4617                 },
   4618             }
   4619             const want_ty = try pt.arrayType(.{
   4620                 .len = val_ty.arrayLen(zcu),
   4621                 .child = elem_ty.toIntern(),
   4622                 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
   4623             });
   4624             return sema.coerce(block, want_ty, uncoerced_val, src);
   4625         },
   4626         .c => {
   4627             // There's nothing meaningful to do here, because we don't know if this is meant to be a
   4628             // single-pointer or a many-pointer.
   4629             return uncoerced_val;
   4630         },
   4631     }
   4632 }
   4633 
   4634 fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
   4635     const pt = sema.pt;
   4636     const zcu = pt.zcu;
   4637     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4638     const src = block.nodeOffset(un_node.src_node);
   4639 
   4640     const operand_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type;
   4641 
   4642     const payload_ty = if (is_ref) ty: {
   4643         if (!operand_ty.isSinglePointer(zcu)) {
   4644             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`.
   4645         }
   4646         break :ty operand_ty.childType(zcu);
   4647     } else operand_ty;
   4648 
   4649     const err_set_ty: Type = err_set: {
   4650         // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals
   4651         // until we hit an error union or set.
   4652         var cur_ty = sema.fn_ret_ty;
   4653         while (true) {
   4654             switch (cur_ty.zigTypeTag(zcu)) {
   4655                 .error_set => break :err_set cur_ty,
   4656                 .error_union => break :err_set cur_ty.errorUnionSet(zcu),
   4657                 .optional => cur_ty = cur_ty.optionalChild(zcu),
   4658                 else => {
   4659                     // This function cannot return an error.
   4660                     // `try` is still valid if the error case is impossible, i.e. no error is returned.
   4661                     // So, the result type has an error set of `error{}`.
   4662                     break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(zcu.gpa, pt.tid, &.{}));
   4663                 },
   4664             }
   4665         }
   4666     };
   4667 
   4668     const eu_ty = try pt.errorUnionType(err_set_ty, payload_ty);
   4669 
   4670     if (is_ref) {
   4671         var ptr_info = operand_ty.ptrInfo(zcu);
   4672         ptr_info.child = eu_ty.toIntern();
   4673         const eu_ptr_ty = try pt.ptrTypeSema(ptr_info);
   4674         return Air.internedToRef(eu_ptr_ty.toIntern());
   4675     } else {
   4676         return Air.internedToRef(eu_ty.toIntern());
   4677     }
   4678 }
   4679 
   4680 fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   4681     const pt = sema.pt;
   4682     const zcu = pt.zcu;
   4683     const un_tok = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
   4684     const src = block.tokenOffset(un_tok.src_tok);
   4685     // In case of GenericPoison, we don't actually have a type, so this will be
   4686     // treated as an untyped address-of operator.
   4687     const ty_operand = try sema.resolveTypeOrPoison(block, src, un_tok.operand) orelse return;
   4688     if (ty_operand.optEuBaseType(zcu).zigTypeTag(zcu) != .pointer) {
   4689         return sema.failWithOwnedErrorMsg(block, msg: {
   4690             const msg = try sema.errMsg(src, "expected type '{f}', found pointer", .{ty_operand.fmt(pt)});
   4691             errdefer msg.destroy(sema.gpa);
   4692             try sema.errNote(src, msg, "address-of operator always returns a pointer", .{});
   4693             break :msg msg;
   4694         });
   4695     }
   4696 }
   4697 
   4698 fn zirValidateConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   4699     if (!block.isComptime()) return;
   4700 
   4701     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4702     const src = block.nodeOffset(un_node.src_node);
   4703     const init_ref = try sema.resolveInst(un_node.operand);
   4704     if (!try sema.isComptimeKnown(init_ref)) {
   4705         return sema.failWithNeededComptime(block, src, null);
   4706     }
   4707 }
   4708 
   4709 fn zirValidateArrayInitRefTy(
   4710     sema: *Sema,
   4711     block: *Block,
   4712     inst: Zir.Inst.Index,
   4713 ) CompileError!Air.Inst.Ref {
   4714     const pt = sema.pt;
   4715     const zcu = pt.zcu;
   4716     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4717     const src = block.nodeOffset(pl_node.src_node);
   4718     const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
   4719     const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.ptr_ty) orelse return .generic_poison_type;
   4720     const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu);
   4721     assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
   4722     switch (zcu.intern_pool.indexToKey(ptr_ty.toIntern())) {
   4723         .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
   4724             .slice, .many => {
   4725                 // Use array of correct length
   4726                 const arr_ty = try pt.arrayType(.{
   4727                     .len = extra.elem_count,
   4728                     .child = ptr_ty.childType(zcu).toIntern(),
   4729                     .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
   4730                 });
   4731                 return Air.internedToRef(arr_ty.toIntern());
   4732             },
   4733             else => {},
   4734         },
   4735         else => {},
   4736     }
   4737     // Otherwise, we just want the pointer child type
   4738     const ret_ty = ptr_ty.childType(zcu);
   4739     if (ret_ty.toIntern() == .anyopaque_type) {
   4740         // The actual array type is unknown, which we represent with a generic poison.
   4741         return .generic_poison_type;
   4742     }
   4743     const arr_ty = ret_ty.optEuBaseType(zcu);
   4744     try sema.validateArrayInitTy(block, src, src, extra.elem_count, arr_ty);
   4745     return Air.internedToRef(ret_ty.toIntern());
   4746 }
   4747 
   4748 fn zirValidateArrayInitTy(
   4749     sema: *Sema,
   4750     block: *Block,
   4751     inst: Zir.Inst.Index,
   4752     is_result_ty: bool,
   4753 ) CompileError!void {
   4754     const pt = sema.pt;
   4755     const zcu = pt.zcu;
   4756     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4757     const src = block.nodeOffset(inst_data.src_node);
   4758     const ty_src: LazySrcLoc = if (is_result_ty) src else block.src(.{ .node_offset_init_ty = inst_data.src_node });
   4759     const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
   4760     // It's okay for the type to be poison: this will result in an anonymous array init.
   4761     const ty = try sema.resolveTypeOrPoison(block, ty_src, extra.ty) orelse return;
   4762     const arr_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty;
   4763     return sema.validateArrayInitTy(block, src, ty_src, extra.init_count, arr_ty);
   4764 }
   4765 
   4766 fn validateArrayInitTy(
   4767     sema: *Sema,
   4768     block: *Block,
   4769     src: LazySrcLoc,
   4770     ty_src: LazySrcLoc,
   4771     init_count: u32,
   4772     ty: Type,
   4773 ) CompileError!void {
   4774     const pt = sema.pt;
   4775     const zcu = pt.zcu;
   4776     switch (ty.zigTypeTag(zcu)) {
   4777         .array => {
   4778             const array_len = ty.arrayLen(zcu);
   4779             if (init_count != array_len) {
   4780                 return sema.fail(block, src, "expected {d} array elements; found {d}", .{
   4781                     array_len, init_count,
   4782                 });
   4783             }
   4784             return;
   4785         },
   4786         .vector => {
   4787             const array_len = ty.arrayLen(zcu);
   4788             if (init_count != array_len) {
   4789                 return sema.fail(block, src, "expected {d} vector elements; found {d}", .{
   4790                     array_len, init_count,
   4791                 });
   4792             }
   4793             return;
   4794         },
   4795         .@"struct" => if (ty.isTuple(zcu)) {
   4796             try ty.resolveFields(pt);
   4797             const array_len = ty.arrayLen(zcu);
   4798             if (init_count > array_len) {
   4799                 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{
   4800                     array_len, init_count,
   4801                 });
   4802             }
   4803             return;
   4804         },
   4805         else => {},
   4806     }
   4807     return sema.failWithArrayInitNotSupported(block, ty_src, ty);
   4808 }
   4809 
   4810 fn zirValidateStructInitTy(
   4811     sema: *Sema,
   4812     block: *Block,
   4813     inst: Zir.Inst.Index,
   4814     is_result_ty: bool,
   4815 ) CompileError!void {
   4816     const pt = sema.pt;
   4817     const zcu = pt.zcu;
   4818     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   4819     const src = block.nodeOffset(inst_data.src_node);
   4820     // It's okay for the type to be poison: this will result in an anonymous struct init.
   4821     const ty = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse return;
   4822     const struct_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty;
   4823 
   4824     switch (struct_ty.zigTypeTag(zcu)) {
   4825         .@"struct", .@"union" => return,
   4826         else => {},
   4827     }
   4828     return sema.failWithStructInitNotSupported(block, src, struct_ty);
   4829 }
   4830 
   4831 fn zirValidatePtrStructInit(
   4832     sema: *Sema,
   4833     block: *Block,
   4834     inst: Zir.Inst.Index,
   4835 ) CompileError!void {
   4836     const tracy = trace(@src());
   4837     defer tracy.end();
   4838 
   4839     const pt = sema.pt;
   4840     const zcu = pt.zcu;
   4841     const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4842     const init_src = block.nodeOffset(validate_inst.src_node);
   4843     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
   4844     const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
   4845     const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node;
   4846     const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4847     const object_ptr = try sema.resolveInst(field_ptr_extra.lhs);
   4848     const agg_ty = sema.typeOf(object_ptr).childType(zcu).optEuBaseType(zcu);
   4849     switch (agg_ty.zigTypeTag(zcu)) {
   4850         .@"struct" => return sema.validateStructInit(
   4851             block,
   4852             agg_ty,
   4853             init_src,
   4854             instrs,
   4855             object_ptr,
   4856         ),
   4857         .@"union" => return sema.validateUnionInit(
   4858             block,
   4859             agg_ty,
   4860             init_src,
   4861             instrs,
   4862         ),
   4863         else => unreachable,
   4864     }
   4865 }
   4866 
   4867 fn validateUnionInit(
   4868     sema: *Sema,
   4869     block: *Block,
   4870     union_ty: Type,
   4871     init_src: LazySrcLoc,
   4872     instrs: []const Zir.Inst.Index,
   4873 ) CompileError!void {
   4874     if (instrs.len == 1) {
   4875         // Trvial validation done, and the union tag was already set by machinery in `unionFieldPtr`.
   4876         return;
   4877     }
   4878     const msg = msg: {
   4879         const msg = try sema.errMsg(
   4880             init_src,
   4881             "cannot initialize multiple union fields at once; unions can only have one active field",
   4882             .{},
   4883         );
   4884         errdefer msg.destroy(sema.gpa);
   4885 
   4886         for (instrs[1..]) |inst| {
   4887             const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   4888             const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node });
   4889             try sema.errNote(inst_src, msg, "additional initializer here", .{});
   4890         }
   4891         try sema.addDeclaredHereNote(msg, union_ty);
   4892         break :msg msg;
   4893     };
   4894     return sema.failWithOwnedErrorMsg(block, msg);
   4895 }
   4896 
   4897 fn validateStructInit(
   4898     sema: *Sema,
   4899     block: *Block,
   4900     struct_ty: Type,
   4901     init_src: LazySrcLoc,
   4902     instrs: []const Zir.Inst.Index,
   4903     struct_ptr: Air.Inst.Ref,
   4904 ) CompileError!void {
   4905     const pt = sema.pt;
   4906     const zcu = pt.zcu;
   4907     const gpa = sema.gpa;
   4908     const ip = &zcu.intern_pool;
   4909 
   4910     // Tracks whether each field was explicitly initialized.
   4911     const found_fields = try gpa.alloc(bool, struct_ty.structFieldCount(zcu));
   4912     defer gpa.free(found_fields);
   4913     @memset(found_fields, false);
   4914 
   4915     for (instrs) |field_ptr| {
   4916         const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
   4917         const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });
   4918         const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
   4919         const field_name = try ip.getOrPutString(
   4920             gpa,
   4921             pt.tid,
   4922             sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
   4923             .no_embedded_nulls,
   4924         );
   4925         const field_index = if (struct_ty.isTuple(zcu))
   4926             try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
   4927         else
   4928             try sema.structFieldIndex(block, struct_ty, field_name, field_src);
   4929         assert(found_fields[field_index] == false);
   4930         found_fields[field_index] = true;
   4931     }
   4932 
   4933     // Our job is simply to deal with default field values. Specifically, any field which was not
   4934     // explicitly initialized must have its default value stored to the field pointer, or, if the
   4935     // field has no default value, a compile error must be emitted instead.
   4936 
   4937     // In the past, this code had other responsibilities, which involved some nasty AIR rewrites. However,
   4938     // that work was actually all redundant:
   4939     //
   4940     // * If the struct value is comptime-known, field stores remain a perfectly valid way of initializing
   4941     //   the struct through RLS; there is no need to turn the field stores into one store. Comptime-known
   4942     //   consts are handled correctly either way thanks to `maybe_comptime_allocs` and friends.
   4943     //
   4944     // * If the struct type is comptime-only, we need to make sure all of the fields were comptime-known.
   4945     //   But the comptime-only type means that `struct_ptr` must be a comptime-mutable pointer, so the
   4946     //   field stores were to comptime-mutable pointers, so have already errored if not comptime-known.
   4947     //
   4948     // * If the value is runtime-known, then comptime-known fields must be validated as runtime values.
   4949     //   But this was already handled for every field store by the machinery in `checkComptimeKnownStore`.
   4950 
   4951     var root_msg: ?*Zcu.ErrorMsg = null;
   4952     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
   4953 
   4954     for (found_fields, 0..) |explicit, i_usize| {
   4955         if (explicit) continue;
   4956         const i: u32 = @intCast(i_usize);
   4957 
   4958         try struct_ty.resolveStructFieldInits(pt);
   4959         const default_val = struct_ty.structFieldDefaultValue(i, zcu);
   4960         if (default_val.toIntern() == .unreachable_value) {
   4961             const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
   4962                 const template = "missing tuple field with index {d}";
   4963                 if (root_msg) |msg| {
   4964                     try sema.errNote(init_src, msg, template, .{i});
   4965                 } else {
   4966                     root_msg = try sema.errMsg(init_src, template, .{i});
   4967                 }
   4968                 continue;
   4969             };
   4970             const template = "missing struct field: {f}";
   4971             const args = .{field_name.fmt(ip)};
   4972             if (root_msg) |msg| {
   4973                 try sema.errNote(init_src, msg, template, args);
   4974             } else {
   4975                 root_msg = try sema.errMsg(init_src, template, args);
   4976             }
   4977             continue;
   4978         }
   4979 
   4980         const field_src = init_src; // TODO better source location
   4981         const default_field_ptr = if (struct_ty.isTuple(zcu))
   4982             try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(i), true)
   4983         else
   4984             try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
   4985         try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr);
   4986         try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store);
   4987     }
   4988 
   4989     if (root_msg) |msg| {
   4990         try sema.addDeclaredHereNote(msg, struct_ty);
   4991         root_msg = null;
   4992         return sema.failWithOwnedErrorMsg(block, msg);
   4993     }
   4994 }
   4995 
   4996 fn zirValidatePtrArrayInit(
   4997     sema: *Sema,
   4998     block: *Block,
   4999     inst: Zir.Inst.Index,
   5000 ) CompileError!void {
   5001     const pt = sema.pt;
   5002     const zcu = pt.zcu;
   5003     const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5004     const init_src = block.nodeOffset(validate_inst.src_node);
   5005     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
   5006     const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
   5007     const first_elem_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node;
   5008     const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data;
   5009     const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr);
   5010     const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu);
   5011     const array_len = array_ty.arrayLen(zcu);
   5012 
   5013     // Analagously to `validateStructInit`, our job is to handle default fields; either emitting AIR
   5014     // to initialize them, or emitting a compile error if an unspecified field has no default. For
   5015     // tuples, there are literally default field values, although they're guaranteed to be comptime
   5016     // fields so we don't need to initialize them. For arrays, we may have a sentinel, which is never
   5017     // specified so we always need to initialize here. For vectors, there's no such thing.
   5018 
   5019     switch (array_ty.zigTypeTag(zcu)) {
   5020         .@"struct" => if (instrs.len != array_len) {
   5021             var root_msg: ?*Zcu.ErrorMsg = null;
   5022             errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
   5023 
   5024             try array_ty.resolveStructFieldInits(pt);
   5025             var i = instrs.len;
   5026             while (i < array_len) : (i += 1) {
   5027                 const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern();
   5028                 if (default_val == .unreachable_value) {
   5029                     const template = "missing tuple field with index {d}";
   5030                     if (root_msg) |msg| {
   5031                         try sema.errNote(init_src, msg, template, .{i});
   5032                     } else {
   5033                         root_msg = try sema.errMsg(init_src, template, .{i});
   5034                     }
   5035                     continue;
   5036                 }
   5037             }
   5038 
   5039             if (root_msg) |msg| {
   5040                 root_msg = null;
   5041                 return sema.failWithOwnedErrorMsg(block, msg);
   5042             }
   5043         },
   5044 
   5045         .array => if (instrs.len != array_len) {
   5046             return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
   5047                 array_len, instrs.len,
   5048             });
   5049         } else if (array_ty.sentinel(zcu)) |sentinel| {
   5050             const array_len_ref = try pt.intRef(.usize, array_len);
   5051             const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true);
   5052             try sema.checkKnownAllocPtr(block, array_ptr, sentinel_ptr);
   5053             try sema.storePtr2(block, init_src, sentinel_ptr, init_src, .fromValue(sentinel), init_src, .store);
   5054         },
   5055 
   5056         .vector => if (instrs.len != array_len) {
   5057             return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
   5058                 array_len, instrs.len,
   5059             });
   5060         },
   5061 
   5062         else => unreachable,
   5063     }
   5064 }
   5065 
   5066 fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5067     const pt = sema.pt;
   5068     const zcu = pt.zcu;
   5069     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5070     const src = block.nodeOffset(inst_data.src_node);
   5071     const operand = try sema.resolveInst(inst_data.operand);
   5072     const operand_ty = sema.typeOf(operand);
   5073 
   5074     if (operand_ty.zigTypeTag(zcu) != .pointer) {
   5075         return sema.fail(block, src, "cannot dereference non-pointer type '{f}'", .{operand_ty.fmt(pt)});
   5076     } else switch (operand_ty.ptrSize(zcu)) {
   5077         .one, .c => {},
   5078         .many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{f}'", .{operand_ty.fmt(pt)}),
   5079         .slice => return sema.fail(block, src, "index syntax required for slice type '{f}'", .{operand_ty.fmt(pt)}),
   5080     }
   5081 
   5082     if ((try sema.typeHasOnePossibleValue(operand_ty.childType(zcu))) != null) {
   5083         // No need to validate the actual pointer value, we don't need it!
   5084         return;
   5085     }
   5086 
   5087     const elem_ty = operand_ty.elemType2(zcu);
   5088     if (try sema.resolveValue(operand)) |val| {
   5089         if (val.isUndef(zcu)) {
   5090             return sema.fail(block, src, "cannot dereference undefined value", .{});
   5091         }
   5092     } else if (try elem_ty.comptimeOnlySema(pt)) {
   5093         const msg = msg: {
   5094             const msg = try sema.errMsg(
   5095                 src,
   5096                 "values of type '{f}' must be comptime-known, but operand value is runtime-known",
   5097                 .{elem_ty.fmt(pt)},
   5098             );
   5099             errdefer msg.destroy(sema.gpa);
   5100 
   5101             try sema.explainWhyTypeIsComptime(msg, src, elem_ty);
   5102             break :msg msg;
   5103         };
   5104         return sema.failWithOwnedErrorMsg(block, msg);
   5105     }
   5106 }
   5107 
   5108 fn typeIsDestructurable(ty: Type, zcu: *const Zcu) bool {
   5109     return switch (ty.zigTypeTag(zcu)) {
   5110         .array, .vector => true,
   5111         .@"struct" => ty.isTuple(zcu),
   5112         else => false,
   5113     };
   5114 }
   5115 
   5116 fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5117     const pt = sema.pt;
   5118     const zcu = pt.zcu;
   5119     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5120     const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data;
   5121     const src = block.nodeOffset(inst_data.src_node);
   5122     const destructure_src = block.nodeOffset(extra.destructure_node);
   5123     const operand = try sema.resolveInst(extra.operand);
   5124     const operand_ty = sema.typeOf(operand);
   5125 
   5126     if (!typeIsDestructurable(operand_ty, zcu)) {
   5127         return sema.failWithOwnedErrorMsg(block, msg: {
   5128             const msg = try sema.errMsg(src, "type '{f}' cannot be destructured", .{operand_ty.fmt(pt)});
   5129             errdefer msg.destroy(sema.gpa);
   5130             try sema.errNote(destructure_src, msg, "result destructured here", .{});
   5131             if (operand_ty.zigTypeTag(pt.zcu) == .error_union) {
   5132                 const base_op_ty = operand_ty.errorUnionPayload(zcu);
   5133                 if (typeIsDestructurable(base_op_ty, zcu))
   5134                     try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
   5135             }
   5136             break :msg msg;
   5137         });
   5138     }
   5139 
   5140     if (operand_ty.arrayLen(zcu) != extra.expect_len) {
   5141         return sema.failWithOwnedErrorMsg(block, msg: {
   5142             const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{
   5143                 extra.expect_len, operand_ty.arrayLen(zcu),
   5144             });
   5145             errdefer msg.destroy(sema.gpa);
   5146             try sema.errNote(destructure_src, msg, "result destructured here", .{});
   5147             break :msg msg;
   5148         });
   5149     }
   5150 }
   5151 
   5152 fn failWithBadMemberAccess(
   5153     sema: *Sema,
   5154     block: *Block,
   5155     agg_ty: Type,
   5156     field_src: LazySrcLoc,
   5157     field_name: InternPool.NullTerminatedString,
   5158 ) CompileError {
   5159     const pt = sema.pt;
   5160     const zcu = pt.zcu;
   5161     const ip = &zcu.intern_pool;
   5162     const kw_name = switch (agg_ty.zigTypeTag(zcu)) {
   5163         .@"union" => "union",
   5164         .@"struct" => "struct",
   5165         .@"opaque" => "opaque",
   5166         .@"enum" => "enum",
   5167         else => unreachable,
   5168     };
   5169     if (agg_ty.typeDeclInst(zcu)) |inst| if ((inst.resolve(ip) orelse return error.AnalysisFail) == .main_struct_inst) {
   5170         return sema.fail(block, field_src, "root source file struct '{f}' has no member named '{f}'", .{
   5171             agg_ty.fmt(pt), field_name.fmt(ip),
   5172         });
   5173     };
   5174 
   5175     return sema.fail(block, field_src, "{s} '{f}' has no member named '{f}'", .{
   5176         kw_name, agg_ty.fmt(pt), field_name.fmt(ip),
   5177     });
   5178 }
   5179 
   5180 fn failWithBadStructFieldAccess(
   5181     sema: *Sema,
   5182     block: *Block,
   5183     struct_ty: Type,
   5184     struct_type: InternPool.LoadedStructType,
   5185     field_src: LazySrcLoc,
   5186     field_name: InternPool.NullTerminatedString,
   5187 ) CompileError {
   5188     const pt = sema.pt;
   5189     const zcu = pt.zcu;
   5190     const ip = &zcu.intern_pool;
   5191 
   5192     const msg = msg: {
   5193         const msg = try sema.errMsg(
   5194             field_src,
   5195             "no field named '{f}' in struct '{f}'",
   5196             .{ field_name.fmt(ip), struct_type.name.fmt(ip) },
   5197         );
   5198         errdefer msg.destroy(sema.gpa);
   5199         try sema.errNote(struct_ty.srcLoc(zcu), msg, "struct declared here", .{});
   5200         break :msg msg;
   5201     };
   5202     return sema.failWithOwnedErrorMsg(block, msg);
   5203 }
   5204 
   5205 fn failWithBadUnionFieldAccess(
   5206     sema: *Sema,
   5207     block: *Block,
   5208     union_ty: Type,
   5209     union_obj: InternPool.LoadedUnionType,
   5210     field_src: LazySrcLoc,
   5211     field_name: InternPool.NullTerminatedString,
   5212 ) CompileError {
   5213     const pt = sema.pt;
   5214     const zcu = pt.zcu;
   5215     const ip = &zcu.intern_pool;
   5216     const gpa = sema.gpa;
   5217 
   5218     const msg = msg: {
   5219         const msg = try sema.errMsg(
   5220             field_src,
   5221             "no field named '{f}' in union '{f}'",
   5222             .{ field_name.fmt(ip), union_obj.name.fmt(ip) },
   5223         );
   5224         errdefer msg.destroy(gpa);
   5225         try sema.errNote(union_ty.srcLoc(zcu), msg, "union declared here", .{});
   5226         break :msg msg;
   5227     };
   5228     return sema.failWithOwnedErrorMsg(block, msg);
   5229 }
   5230 
   5231 fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) !void {
   5232     const zcu = sema.pt.zcu;
   5233     const src_loc = decl_ty.srcLocOrNull(zcu) orelse return;
   5234     const category = switch (decl_ty.zigTypeTag(zcu)) {
   5235         .@"union" => "union",
   5236         .@"struct" => "struct",
   5237         .@"enum" => "enum",
   5238         .@"opaque" => "opaque",
   5239         else => unreachable,
   5240     };
   5241     try sema.errNote(src_loc, parent, "{s} declared here", .{category});
   5242 }
   5243 
   5244 fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5245     const tracy = trace(@src());
   5246     defer tracy.end();
   5247 
   5248     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5249     const src = block.nodeOffset(pl_node.src_node);
   5250     const bin = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data;
   5251     const ptr = try sema.resolveInst(bin.lhs);
   5252     const operand = try sema.resolveInst(bin.rhs);
   5253     const ptr_inst = ptr.toIndex().?;
   5254     const air_datas = sema.air_instructions.items(.data);
   5255 
   5256     switch (sema.air_instructions.items(.tag)[@intFromEnum(ptr_inst)]) {
   5257         .inferred_alloc_comptime => {
   5258             const iac = &air_datas[@intFromEnum(ptr_inst)].inferred_alloc_comptime;
   5259             return sema.storeToInferredAllocComptime(block, src, operand, iac);
   5260         },
   5261         .inferred_alloc => {
   5262             const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?;
   5263             return sema.storeToInferredAlloc(block, src, ptr, operand, ia);
   5264         },
   5265         else => unreachable,
   5266     }
   5267 }
   5268 
   5269 fn storeToInferredAlloc(
   5270     sema: *Sema,
   5271     block: *Block,
   5272     src: LazySrcLoc,
   5273     ptr: Air.Inst.Ref,
   5274     operand: Air.Inst.Ref,
   5275     inferred_alloc: *InferredAlloc,
   5276 ) CompileError!void {
   5277     // Create a store instruction as a placeholder.  This will be replaced by a
   5278     // proper store sequence once we know the stored type.
   5279     const dummy_store = try block.addBinOp(.store, ptr, operand);
   5280     try sema.checkComptimeKnownStore(block, dummy_store, src);
   5281     // Add the stored instruction to the set we will use to resolve peer types
   5282     // for the inferred allocation.
   5283     try inferred_alloc.prongs.append(sema.arena, dummy_store.toIndex().?);
   5284 }
   5285 
   5286 fn storeToInferredAllocComptime(
   5287     sema: *Sema,
   5288     block: *Block,
   5289     src: LazySrcLoc,
   5290     operand: Air.Inst.Ref,
   5291     iac: *Air.Inst.Data.InferredAllocComptime,
   5292 ) CompileError!void {
   5293     const pt = sema.pt;
   5294     const zcu = pt.zcu;
   5295     const operand_ty = sema.typeOf(operand);
   5296     // There will be only one store_to_inferred_ptr because we are running at comptime.
   5297     // The alloc will turn into a Decl or a ComptimeAlloc.
   5298     const operand_val = try sema.resolveValue(operand) orelse {
   5299         return sema.failWithNeededComptime(block, src, .{ .simple = .stored_to_comptime_var });
   5300     };
   5301     const alloc_ty = try pt.ptrTypeSema(.{
   5302         .child = operand_ty.toIntern(),
   5303         .flags = .{
   5304             .alignment = iac.alignment,
   5305             .is_const = iac.is_const,
   5306         },
   5307     });
   5308     if (iac.is_const and !operand_val.canMutateComptimeVarState(zcu)) {
   5309         iac.ptr = try pt.intern(.{ .ptr = .{
   5310             .ty = alloc_ty.toIntern(),
   5311             .base_addr = .{ .uav = .{
   5312                 .val = operand_val.toIntern(),
   5313                 .orig_ty = alloc_ty.toIntern(),
   5314             } },
   5315             .byte_offset = 0,
   5316         } });
   5317     } else {
   5318         const alloc_index = try sema.newComptimeAlloc(block, src, operand_ty, iac.alignment);
   5319         sema.getComptimeAlloc(alloc_index).val = .{ .interned = operand_val.toIntern() };
   5320         iac.ptr = try pt.intern(.{ .ptr = .{
   5321             .ty = alloc_ty.toIntern(),
   5322             .base_addr = .{ .comptime_alloc = alloc_index },
   5323             .byte_offset = 0,
   5324         } });
   5325     }
   5326 }
   5327 
   5328 fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5329     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5330     const src = block.nodeOffset(inst_data.src_node);
   5331     const quota: u32 = @intCast(try sema.resolveInt(block, src, inst_data.operand, .u32, .{ .simple = .operand_setEvalBranchQuota }));
   5332     sema.branch_quota = @max(sema.branch_quota, quota);
   5333     sema.allow_memoize = false;
   5334 }
   5335 
   5336 fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5337     const tracy = trace(@src());
   5338     defer tracy.end();
   5339 
   5340     const zir_tags = sema.code.instructions.items(.tag);
   5341     const zir_datas = sema.code.instructions.items(.data);
   5342     const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
   5343     const src = block.nodeOffset(inst_data.src_node);
   5344     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   5345     const ptr = try sema.resolveInst(extra.lhs);
   5346     const operand = try sema.resolveInst(extra.rhs);
   5347 
   5348     const is_ret = if (extra.lhs.toIndex()) |ptr_index|
   5349         zir_tags[@intFromEnum(ptr_index)] == .ret_ptr
   5350     else
   5351         false;
   5352 
   5353     const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
   5354     const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node });
   5355     const air_tag: Air.Inst.Tag = if (is_ret)
   5356         .ret_ptr
   5357     else if (block.wantSafety())
   5358         .store_safe
   5359     else
   5360         .store;
   5361     return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
   5362 }
   5363 
   5364 fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5365     const bytes = sema.code.instructions.items(.data)[@intFromEnum(inst)].str.get(sema.code);
   5366     return sema.addStrLit(
   5367         try sema.pt.zcu.intern_pool.getOrPutString(sema.gpa, sema.pt.tid, bytes, .maybe_embedded_nulls),
   5368         bytes.len,
   5369     );
   5370 }
   5371 
   5372 fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) CompileError!Air.Inst.Ref {
   5373     return sema.addStrLit(string.toString(), string.length(&sema.pt.zcu.intern_pool));
   5374 }
   5375 
   5376 pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref {
   5377     const pt = sema.pt;
   5378     const array_ty = try pt.arrayType(.{
   5379         .len = len,
   5380         .sentinel = .zero_u8,
   5381         .child = .u8_type,
   5382     });
   5383     const val = try pt.intern(.{ .aggregate = .{
   5384         .ty = array_ty.toIntern(),
   5385         .storage = .{ .bytes = string },
   5386     } });
   5387     return sema.uavRef(val);
   5388 }
   5389 
   5390 fn uavRef(sema: *Sema, val: InternPool.Index) CompileError!Air.Inst.Ref {
   5391     return Air.internedToRef(try sema.pt.refValue(val));
   5392 }
   5393 
   5394 fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5395     _ = block;
   5396     const tracy = trace(@src());
   5397     defer tracy.end();
   5398 
   5399     const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].int;
   5400     return sema.pt.intRef(.comptime_int, int);
   5401 }
   5402 
   5403 fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5404     _ = block;
   5405     const tracy = trace(@src());
   5406     defer tracy.end();
   5407 
   5408     const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].str;
   5409     const byte_count = int.len * @sizeOf(std.math.big.Limb);
   5410     const limb_bytes = sema.code.string_bytes[@intFromEnum(int.start)..][0..byte_count];
   5411 
   5412     // TODO: this allocation and copy is only needed because the limbs may be unaligned.
   5413     // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these
   5414     // two lines can be removed.
   5415     const limbs = try sema.arena.alloc(std.math.big.Limb, int.len);
   5416     @memcpy(mem.sliceAsBytes(limbs), limb_bytes);
   5417 
   5418     return Air.internedToRef((try sema.pt.intValue_big(.comptime_int, .{
   5419         .limbs = limbs,
   5420         .positive = true,
   5421     })).toIntern());
   5422 }
   5423 
   5424 fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5425     _ = block;
   5426     const number = sema.code.instructions.items(.data)[@intFromEnum(inst)].float;
   5427     return Air.internedToRef((try sema.pt.floatValue(
   5428         .comptime_float,
   5429         number,
   5430     )).toIntern());
   5431 }
   5432 
   5433 fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5434     _ = block;
   5435     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5436     const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
   5437     const number = extra.get();
   5438     return Air.internedToRef((try sema.pt.floatValue(.comptime_float, number)).toIntern());
   5439 }
   5440 
   5441 fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5442     const tracy = trace(@src());
   5443     defer tracy.end();
   5444 
   5445     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5446     const src = block.nodeOffset(inst_data.src_node);
   5447     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   5448     const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .compile_error_string });
   5449     return sema.fail(block, src, "{s}", .{msg});
   5450 }
   5451 
   5452 fn zirCompileLog(
   5453     sema: *Sema,
   5454     block: *Block,
   5455     extended: Zir.Inst.Extended.InstData,
   5456 ) CompileError!Air.Inst.Ref {
   5457     const pt = sema.pt;
   5458     const zcu = pt.zcu;
   5459     const gpa = zcu.gpa;
   5460 
   5461     var aw: std.io.Writer.Allocating = .init(gpa);
   5462     defer aw.deinit();
   5463     const writer = &aw.writer;
   5464 
   5465     const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
   5466     const src_node = extra.data.src_node;
   5467     const args = sema.code.refSlice(extra.end, extended.small);
   5468 
   5469     for (args, 0..) |arg_ref, i| {
   5470         if (i != 0) writer.writeAll(", ") catch return error.OutOfMemory;
   5471 
   5472         const arg = try sema.resolveInst(arg_ref);
   5473         const arg_ty = sema.typeOf(arg);
   5474         if (try sema.resolveValueResolveLazy(arg)) |val| {
   5475             writer.print("@as({f}, {f})", .{
   5476                 arg_ty.fmt(pt), val.fmtValueSema(pt, sema),
   5477             }) catch return error.OutOfMemory;
   5478         } else {
   5479             writer.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory;
   5480         }
   5481     }
   5482 
   5483     const line_data = try zcu.intern_pool.getOrPutString(gpa, pt.tid, aw.getWritten(), .no_embedded_nulls);
   5484 
   5485     const line_idx: Zcu.CompileLogLine.Index = if (zcu.free_compile_log_lines.pop()) |idx| idx: {
   5486         zcu.compile_log_lines.items[@intFromEnum(idx)] = .{
   5487             .next = .none,
   5488             .data = line_data,
   5489         };
   5490         break :idx idx;
   5491     } else idx: {
   5492         try zcu.compile_log_lines.append(gpa, .{
   5493             .next = .none,
   5494             .data = line_data,
   5495         });
   5496         break :idx @enumFromInt(zcu.compile_log_lines.items.len - 1);
   5497     };
   5498 
   5499     const gop = try zcu.compile_logs.getOrPut(gpa, sema.owner);
   5500     if (gop.found_existing) {
   5501         const prev_line = gop.value_ptr.last_line.get(zcu);
   5502         assert(prev_line.next == .none);
   5503         prev_line.next = line_idx.toOptional();
   5504         gop.value_ptr.last_line = line_idx;
   5505     } else {
   5506         gop.value_ptr.* = .{
   5507             .base_node_inst = block.src_base_inst,
   5508             .node_offset = src_node,
   5509             .first_line = line_idx,
   5510             .last_line = line_idx,
   5511         };
   5512     }
   5513     return .void_value;
   5514 }
   5515 
   5516 fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5517     const pt = sema.pt;
   5518     const zcu = pt.zcu;
   5519 
   5520     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   5521     const src = block.nodeOffset(inst_data.src_node);
   5522     const msg_inst = try sema.resolveInst(inst_data.operand);
   5523 
   5524     const coerced_msg = try sema.coerce(block, .slice_const_u8, msg_inst, block.builtinCallArgSrc(inst_data.src_node, 0));
   5525 
   5526     if (block.isComptime()) {
   5527         return sema.fail(block, src, "encountered @panic at comptime", .{});
   5528     }
   5529 
   5530     // We only apply the first hint in a branch.
   5531     // This allows user-provided hints to override implicit cold hints.
   5532     if (sema.branch_hint == null) {
   5533         sema.branch_hint = .cold;
   5534     }
   5535 
   5536     if (!zcu.backendSupportsFeature(.panic_fn)) {
   5537         _ = try block.addNoOp(.trap);
   5538         return;
   5539     }
   5540 
   5541     try sema.ensureMemoizedStateResolved(src, .panic);
   5542     try zcu.ensureFuncBodyAnalysisQueued(zcu.builtin_decl_values.get(.@"panic.call"));
   5543 
   5544     const panic_fn = Air.internedToRef(zcu.builtin_decl_values.get(.@"panic.call"));
   5545 
   5546     const opt_usize_ty = try pt.optionalType(.usize_type);
   5547     const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{
   5548         .ty = opt_usize_ty.toIntern(),
   5549         .val = .none,
   5550     } })));
   5551     try sema.callBuiltin(block, src, panic_fn, .auto, &.{ coerced_msg, null_ret_addr }, .@"@panic");
   5552 }
   5553 
   5554 fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   5555     const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node;
   5556     const src = block.nodeOffset(src_node);
   5557     if (block.isComptime())
   5558         return sema.fail(block, src, "encountered @trap at comptime", .{});
   5559     _ = try block.addNoOp(.trap);
   5560 }
   5561 
   5562 fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5563     const tracy = trace(@src());
   5564     defer tracy.end();
   5565 
   5566     const pt = sema.pt;
   5567     const zcu = pt.zcu;
   5568     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5569     const src = parent_block.nodeOffset(inst_data.src_node);
   5570     const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
   5571     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   5572     const gpa = sema.gpa;
   5573 
   5574     // AIR expects a block outside the loop block too.
   5575     // Reserve space for a Loop instruction so that generated Break instructions can
   5576     // point to it, even if it doesn't end up getting used because the code ends up being
   5577     // comptime evaluated.
   5578     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   5579     const loop_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1);
   5580     try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
   5581     sema.air_instructions.appendAssumeCapacity(.{
   5582         .tag = .block,
   5583         .data = undefined,
   5584     });
   5585     sema.air_instructions.appendAssumeCapacity(.{
   5586         .tag = .loop,
   5587         .data = .{ .ty_pl = .{
   5588             .ty = .noreturn_type,
   5589             .payload = undefined,
   5590         } },
   5591     });
   5592     var label: Block.Label = .{
   5593         .zir_block = inst,
   5594         .merges = .{
   5595             .src_locs = .{},
   5596             .results = .{},
   5597             .br_list = .{},
   5598             .block_inst = block_inst,
   5599         },
   5600     };
   5601     var child_block = parent_block.makeSubBlock();
   5602     child_block.label = &label;
   5603     child_block.runtime_cond = null;
   5604     child_block.runtime_loop = src;
   5605     child_block.runtime_index.increment();
   5606     const merges = &child_block.label.?.merges;
   5607 
   5608     defer child_block.instructions.deinit(gpa);
   5609     defer merges.deinit(gpa);
   5610 
   5611     var loop_block = child_block.makeSubBlock();
   5612     defer loop_block.instructions.deinit(gpa);
   5613 
   5614     // Use `analyzeBodyInner` directly to push any comptime control flow up the stack.
   5615     try sema.analyzeBodyInner(&loop_block, body);
   5616 
   5617     // TODO: since AIR has `repeat` now, we could change ZIR to generate
   5618     // more optimal code utilizing `repeat` instructions across blocks!
   5619     // For now, if the generated loop body does not terminate `noreturn`,
   5620     // then `analyzeBodyInner` is signalling that it ended with `repeat`.
   5621 
   5622     const loop_block_len = loop_block.instructions.items.len;
   5623     if (loop_block_len > 0 and sema.typeOf(loop_block.instructions.items[loop_block_len - 1].toRef()).isNoReturn(zcu)) {
   5624         // If the loop ended with a noreturn terminator, then there is no way for it to loop,
   5625         // so we can just use the block instead.
   5626         try child_block.instructions.appendSlice(gpa, loop_block.instructions.items);
   5627     } else {
   5628         _ = try loop_block.addInst(.{
   5629             .tag = .repeat,
   5630             .data = .{ .repeat = .{
   5631                 .loop_inst = loop_inst,
   5632             } },
   5633         });
   5634         // Note that `loop_block_len` is now off by one.
   5635 
   5636         try child_block.instructions.append(gpa, loop_inst);
   5637 
   5638         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + loop_block_len + 1);
   5639         sema.air_instructions.items(.data)[@intFromEnum(loop_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(
   5640             Air.Block{ .body_len = @intCast(loop_block_len + 1) },
   5641         );
   5642         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items));
   5643     }
   5644     return sema.resolveAnalyzedBlock(parent_block, src, &child_block, merges, false);
   5645 }
   5646 
   5647 fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5648     const tracy = trace(@src());
   5649     defer tracy.end();
   5650 
   5651     const pt = sema.pt;
   5652     const zcu = pt.zcu;
   5653     const comp = zcu.comp;
   5654     const gpa = sema.gpa;
   5655     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5656     const src = parent_block.nodeOffset(pl_node.src_node);
   5657     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
   5658     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   5659 
   5660     // we check this here to avoid undefined symbols
   5661     if (!build_options.have_llvm)
   5662         return sema.fail(parent_block, src, "C import unavailable; Zig compiler built without LLVM extensions", .{});
   5663 
   5664     var c_import_buf = std.ArrayList(u8).init(gpa);
   5665     defer c_import_buf.deinit();
   5666 
   5667     var child_block: Block = .{
   5668         .parent = parent_block,
   5669         .sema = sema,
   5670         .namespace = parent_block.namespace,
   5671         .instructions = .{},
   5672         .inlining = parent_block.inlining,
   5673         .comptime_reason = .{ .reason = .{
   5674             .src = src,
   5675             .r = .{ .simple = .operand_cImport },
   5676         } },
   5677         .c_import_buf = &c_import_buf,
   5678         .runtime_cond = parent_block.runtime_cond,
   5679         .runtime_loop = parent_block.runtime_loop,
   5680         .runtime_index = parent_block.runtime_index,
   5681         .src_base_inst = parent_block.src_base_inst,
   5682         .type_name_ctx = parent_block.type_name_ctx,
   5683     };
   5684     defer child_block.instructions.deinit(gpa);
   5685 
   5686     _ = try sema.analyzeInlineBody(&child_block, body, inst);
   5687 
   5688     var c_import_res = comp.cImport(c_import_buf.items, parent_block.ownerModule()) catch |err|
   5689         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5690     defer c_import_res.deinit(gpa);
   5691 
   5692     if (c_import_res.errors.errorMessageCount() != 0) {
   5693         const msg = msg: {
   5694             const msg = try sema.errMsg(src, "C import failed", .{});
   5695             errdefer msg.destroy(gpa);
   5696 
   5697             if (!comp.config.link_libc)
   5698                 try sema.errNote(src, msg, "libc headers not available; compilation does not link against libc", .{});
   5699 
   5700             const gop = try zcu.cimport_errors.getOrPut(gpa, sema.owner);
   5701             if (!gop.found_existing) {
   5702                 gop.value_ptr.* = c_import_res.errors;
   5703                 c_import_res.errors = std.zig.ErrorBundle.empty;
   5704             }
   5705             break :msg msg;
   5706         };
   5707         return sema.failWithOwnedErrorMsg(&child_block, msg);
   5708     }
   5709     const parent_mod = parent_block.ownerModule();
   5710     const digest = Cache.binToHex(c_import_res.digest);
   5711 
   5712     const new_file_index = file: {
   5713         const c_import_zig_path = try comp.arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ digest);
   5714         const c_import_mod = Package.Module.create(comp.arena, .{
   5715             .paths = .{
   5716                 .root = try .fromRoot(comp.arena, comp.dirs, .local_cache, c_import_zig_path),
   5717                 .root_src_path = "cimport.zig",
   5718             },
   5719             .fully_qualified_name = c_import_zig_path,
   5720             .cc_argv = parent_mod.cc_argv,
   5721             .inherited = .{},
   5722             .global = comp.config,
   5723             .parent = parent_mod,
   5724         }) catch |err| switch (err) {
   5725             // None of these are possible because we are creating a package with
   5726             // the exact same configuration as the parent package, which already
   5727             // passed these checks.
   5728             error.ValgrindUnsupportedOnTarget => unreachable,
   5729             error.TargetRequiresSingleThreaded => unreachable,
   5730             error.BackendRequiresSingleThreaded => unreachable,
   5731             error.TargetRequiresPic => unreachable,
   5732             error.PieRequiresPic => unreachable,
   5733             error.DynamicLinkingRequiresPic => unreachable,
   5734             error.TargetHasNoRedZone => unreachable,
   5735             error.StackCheckUnsupportedByTarget => unreachable,
   5736             error.StackProtectorUnsupportedByTarget => unreachable,
   5737             error.StackProtectorUnavailableWithoutLibC => unreachable,
   5738 
   5739             else => |e| return e,
   5740         };
   5741         const c_import_file_path: Compilation.Path = try c_import_mod.root.join(gpa, comp.dirs, "cimport.zig");
   5742         errdefer c_import_file_path.deinit(gpa);
   5743         const c_import_file = try gpa.create(Zcu.File);
   5744         errdefer gpa.destroy(c_import_file);
   5745         const c_import_file_index = try zcu.intern_pool.createFile(gpa, pt.tid, .{
   5746             .bin_digest = c_import_file_path.digest(),
   5747             .file = c_import_file,
   5748             .root_type = .none,
   5749         });
   5750         c_import_file.* = .{
   5751             .status = .never_loaded,
   5752             .stat = undefined,
   5753             .is_builtin = false,
   5754             .path = c_import_file_path,
   5755             .source = null,
   5756             .tree = null,
   5757             .zir = null,
   5758             .zoir = null,
   5759             .mod = c_import_mod,
   5760             .sub_file_path = "cimport.zig",
   5761             .module_changed = false,
   5762             .prev_zir = null,
   5763             .zoir_invalidated = false,
   5764         };
   5765         break :file c_import_file_index;
   5766     };
   5767     pt.updateFile(new_file_index, zcu.fileByIndex(new_file_index)) catch |err|
   5768         return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
   5769 
   5770     try pt.ensureFileAnalyzed(new_file_index);
   5771     const ty = zcu.fileRootType(new_file_index);
   5772     try sema.declareDependency(.{ .interned = ty });
   5773     try sema.addTypeReferenceEntry(src, ty);
   5774     return Air.internedToRef(ty);
   5775 }
   5776 
   5777 fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5778     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5779     const src = parent_block.nodeOffset(inst_data.src_node);
   5780     return sema.failWithUseOfAsync(parent_block, src);
   5781 }
   5782 
   5783 fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   5784     const tracy = trace(@src());
   5785     defer tracy.end();
   5786 
   5787     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   5788     const src = parent_block.nodeOffset(pl_node.src_node);
   5789     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
   5790     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
   5791     const gpa = sema.gpa;
   5792 
   5793     // Reserve space for a Block instruction so that generated Break instructions can
   5794     // point to it, even if it doesn't end up getting used because the code ends up being
   5795     // comptime evaluated or is an unlabeled block.
   5796     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   5797     try sema.air_instructions.append(gpa, .{
   5798         .tag = .block,
   5799         .data = undefined,
   5800     });
   5801 
   5802     var label: Block.Label = .{
   5803         .zir_block = inst,
   5804         .merges = .{
   5805             .src_locs = .{},
   5806             .results = .{},
   5807             .br_list = .{},
   5808             .block_inst = block_inst,
   5809         },
   5810     };
   5811 
   5812     var child_block: Block = .{
   5813         .parent = parent_block,
   5814         .sema = sema,
   5815         .namespace = parent_block.namespace,
   5816         .instructions = .{},
   5817         .label = &label,
   5818         .inlining = parent_block.inlining,
   5819         .comptime_reason = parent_block.comptime_reason,
   5820         .is_typeof = parent_block.is_typeof,
   5821         .want_safety = parent_block.want_safety,
   5822         .float_mode = parent_block.float_mode,
   5823         .c_import_buf = parent_block.c_import_buf,
   5824         .runtime_cond = parent_block.runtime_cond,
   5825         .runtime_loop = parent_block.runtime_loop,
   5826         .runtime_index = parent_block.runtime_index,
   5827         .error_return_trace_index = parent_block.error_return_trace_index,
   5828         .src_base_inst = parent_block.src_base_inst,
   5829         .type_name_ctx = parent_block.type_name_ctx,
   5830     };
   5831 
   5832     defer child_block.instructions.deinit(gpa);
   5833     defer label.merges.deinit(gpa);
   5834 
   5835     return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
   5836 }
   5837 
   5838 /// Semantically analyze the given ZIR body, emitting any resulting runtime code into the AIR block
   5839 /// specified by `child_block` if necessary (and emitting this block into `parent_block`).
   5840 /// TODO: `merges` is known from `child_block`, remove this parameter.
   5841 fn resolveBlockBody(
   5842     sema: *Sema,
   5843     parent_block: *Block,
   5844     src: LazySrcLoc,
   5845     child_block: *Block,
   5846     body: []const Zir.Inst.Index,
   5847     /// This is the instruction that a break instruction within `body` can
   5848     /// use to return from the body.
   5849     body_inst: Zir.Inst.Index,
   5850     merges: *Block.Merges,
   5851 ) CompileError!Air.Inst.Ref {
   5852     if (child_block.isComptime()) {
   5853         return sema.resolveInlineBody(child_block, body, body_inst);
   5854     } else {
   5855         assert(sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)] == .block);
   5856         var need_debug_scope = false;
   5857         child_block.need_debug_scope = &need_debug_scope;
   5858         if (sema.analyzeBodyInner(child_block, body)) |_| {
   5859             return sema.resolveAnalyzedBlock(parent_block, src, child_block, merges, need_debug_scope);
   5860         } else |err| switch (err) {
   5861             error.ComptimeBreak => {
   5862                 // Comptime control flow is happening, however child_block may still contain
   5863                 // runtime instructions which need to be copied to the parent block.
   5864                 if (need_debug_scope and child_block.instructions.items.len > 0) {
   5865                     // We need a runtime block for scoping reasons.
   5866                     _ = try child_block.addBr(merges.block_inst, .void_value);
   5867                     try parent_block.instructions.append(sema.gpa, merges.block_inst);
   5868                     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).@"struct".fields.len +
   5869                         child_block.instructions.items.len);
   5870                     sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   5871                         .ty = .void_type,
   5872                         .payload = sema.addExtraAssumeCapacity(Air.Block{
   5873                             .body_len = @intCast(child_block.instructions.items.len),
   5874                         }),
   5875                     } };
   5876                     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
   5877                 } else {
   5878                     // We can copy instructions directly to the parent block.
   5879                     try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items);
   5880                 }
   5881 
   5882                 const break_inst = sema.comptime_break_inst;
   5883                 const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
   5884                 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
   5885                 if (extra.block_inst == body_inst) {
   5886                     return try sema.resolveInst(break_data.operand);
   5887                 } else {
   5888                     return error.ComptimeBreak;
   5889                 }
   5890             },
   5891             else => |e| return e,
   5892         }
   5893     }
   5894 }
   5895 
   5896 /// After a body corresponding to an AIR `block` has been analyzed, this function places them into
   5897 /// the block pointed at by `merges.block_inst` if necessary, or the block may be elided in favor of
   5898 /// inlining the instructions directly into the parent block. Either way, it considers all merges of
   5899 /// this block, and combines them appropriately using peer type resolution, returning the final
   5900 /// value of the block.
   5901 fn resolveAnalyzedBlock(
   5902     sema: *Sema,
   5903     parent_block: *Block,
   5904     src: LazySrcLoc,
   5905     child_block: *Block,
   5906     merges: *Block.Merges,
   5907     need_debug_scope: bool,
   5908 ) CompileError!Air.Inst.Ref {
   5909     const tracy = trace(@src());
   5910     defer tracy.end();
   5911 
   5912     const gpa = sema.gpa;
   5913     const pt = sema.pt;
   5914     const zcu = pt.zcu;
   5915 
   5916     // Blocks must terminate with noreturn instruction.
   5917     assert(child_block.instructions.items.len != 0);
   5918     assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(zcu));
   5919 
   5920     const block_tag = sema.air_instructions.items(.tag)[@intFromEnum(merges.block_inst)];
   5921     switch (block_tag) {
   5922         .block => {},
   5923         .dbg_inline_block => assert(need_debug_scope),
   5924         else => unreachable,
   5925     }
   5926     if (merges.results.items.len == 0) {
   5927         switch (block_tag) {
   5928             .block => {
   5929                 // No need for a block instruction. We can put the new instructions
   5930                 // directly into the parent block.
   5931                 if (need_debug_scope) {
   5932                     // The code following this block is unreachable, as the block has no
   5933                     // merges, so we don't necessarily need to emit this as an AIR block.
   5934                     // However, we need a block *somewhere* to make the scoping correct,
   5935                     // so forward this request to the parent block.
   5936                     if (parent_block.need_debug_scope) |ptr| ptr.* = true;
   5937                 }
   5938                 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
   5939                 return child_block.instructions.items[child_block.instructions.items.len - 1].toRef();
   5940             },
   5941             .dbg_inline_block => {
   5942                 // Create a block containing all instruction from the body.
   5943                 try parent_block.instructions.append(gpa, merges.block_inst);
   5944                 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len +
   5945                     child_block.instructions.items.len);
   5946                 sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   5947                     .ty = .noreturn_type,
   5948                     .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
   5949                         .func = child_block.inlining.?.func,
   5950                         .body_len = @intCast(child_block.instructions.items.len),
   5951                     }),
   5952                 } };
   5953                 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
   5954                 return merges.block_inst.toRef();
   5955             },
   5956             else => unreachable,
   5957         }
   5958     }
   5959     if (merges.results.items.len == 1) {
   5960         // If the `break` is trailing, we may be able to elide the AIR block here
   5961         // by appending the new instructions directly to the parent block.
   5962         if (!need_debug_scope) {
   5963             const last_inst_index = child_block.instructions.items.len - 1;
   5964             const last_inst = child_block.instructions.items[last_inst_index];
   5965             if (sema.getBreakBlock(last_inst)) |br_block| {
   5966                 if (br_block == merges.block_inst) {
   5967                     // Great, the last instruction is the break! Put the instructions
   5968                     // directly into the parent block.
   5969                     try parent_block.instructions.appendSlice(gpa, child_block.instructions.items[0..last_inst_index]);
   5970                     return merges.results.items[0];
   5971                 }
   5972             }
   5973         }
   5974         // Okay, we need a runtime block. If the value is comptime-known, the
   5975         // block should just return void, and we return the merge result
   5976         // directly. Otherwise, we can defer to the logic below.
   5977         if (try sema.resolveValue(merges.results.items[0])) |result_val| {
   5978             // Create a block containing all instruction from the body.
   5979             try parent_block.instructions.append(gpa, merges.block_inst);
   5980             switch (block_tag) {
   5981                 .block => {
   5982                     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
   5983                         child_block.instructions.items.len);
   5984                     sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   5985                         .ty = .void_type,
   5986                         .payload = sema.addExtraAssumeCapacity(Air.Block{
   5987                             .body_len = @intCast(child_block.instructions.items.len),
   5988                         }),
   5989                     } };
   5990                 },
   5991                 .dbg_inline_block => {
   5992                     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len +
   5993                         child_block.instructions.items.len);
   5994                     sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   5995                         .ty = .void_type,
   5996                         .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
   5997                             .func = child_block.inlining.?.func,
   5998                             .body_len = @intCast(child_block.instructions.items.len),
   5999                         }),
   6000                     } };
   6001                 },
   6002                 else => unreachable,
   6003             }
   6004             sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
   6005             // Rewrite the break to just give value {}; the value is
   6006             // comptime-known and will be returned directly.
   6007             sema.air_instructions.items(.data)[@intFromEnum(merges.br_list.items[0])].br.operand = .void_value;
   6008             return Air.internedToRef(result_val.toIntern());
   6009         }
   6010     }
   6011     // It is impossible to have the number of results be > 1 in a comptime scope.
   6012     assert(!child_block.isComptime()); // Should already got a compile error in the condbr condition.
   6013 
   6014     // Note that we'll always create an AIR block here, so `need_debug_scope` is irrelevant.
   6015 
   6016     // Need to set the type and emit the Block instruction. This allows machine code generation
   6017     // to emit a jump instruction to after the block when it encounters the break.
   6018     try parent_block.instructions.append(gpa, merges.block_inst);
   6019     const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items });
   6020     // TODO add note "missing else causes void value"
   6021 
   6022     const type_src = src; // TODO: better source location
   6023     if (try resolved_ty.comptimeOnlySema(pt)) {
   6024         const msg = msg: {
   6025             const msg = try sema.errMsg(type_src, "value with comptime-only type '{f}' depends on runtime control flow", .{resolved_ty.fmt(pt)});
   6026             errdefer msg.destroy(sema.gpa);
   6027 
   6028             const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?;
   6029             try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
   6030 
   6031             try sema.explainWhyTypeIsComptime(msg, type_src, resolved_ty);
   6032 
   6033             break :msg msg;
   6034         };
   6035         return sema.failWithOwnedErrorMsg(child_block, msg);
   6036     }
   6037     for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| {
   6038         try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst);
   6039     }
   6040 
   6041     try sema.checkMergeAllowed(child_block, type_src, resolved_ty);
   6042 
   6043     const ty_inst = Air.internedToRef(resolved_ty.toIntern());
   6044     switch (block_tag) {
   6045         .block => {
   6046             try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
   6047                 child_block.instructions.items.len);
   6048             sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   6049                 .ty = ty_inst,
   6050                 .payload = sema.addExtraAssumeCapacity(Air.Block{
   6051                     .body_len = @intCast(child_block.instructions.items.len),
   6052                 }),
   6053             } };
   6054         },
   6055         .dbg_inline_block => {
   6056             try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".fields.len +
   6057                 child_block.instructions.items.len);
   6058             sema.air_instructions.items(.data)[@intFromEnum(merges.block_inst)] = .{ .ty_pl = .{
   6059                 .ty = ty_inst,
   6060                 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
   6061                     .func = child_block.inlining.?.func,
   6062                     .body_len = @intCast(child_block.instructions.items.len),
   6063                 }),
   6064             } };
   6065         },
   6066         else => unreachable,
   6067     }
   6068     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
   6069     // Now that the block has its type resolved, we need to go back into all the break
   6070     // instructions, and insert type coercion on the operands.
   6071     for (merges.br_list.items) |br| {
   6072         const br_operand = sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand;
   6073         const br_operand_src = src;
   6074         const br_operand_ty = sema.typeOf(br_operand);
   6075         if (br_operand_ty.eql(resolved_ty, zcu)) {
   6076             // No type coercion needed.
   6077             continue;
   6078         }
   6079         var coerce_block = parent_block.makeSubBlock();
   6080         defer coerce_block.instructions.deinit(gpa);
   6081         const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src);
   6082         // If no instructions were produced, such as in the case of a coercion of a
   6083         // constant value to a new type, we can simply point the br operand to it.
   6084         if (coerce_block.instructions.items.len == 0) {
   6085             sema.air_instructions.items(.data)[@intFromEnum(br)].br.operand = coerced_operand;
   6086             continue;
   6087         }
   6088         assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1].toRef() == coerced_operand);
   6089 
   6090         // Convert the br instruction to a block instruction that has the coercion
   6091         // and then a new br inside that returns the coerced instruction.
   6092         const sub_block_len: u32 = @intCast(coerce_block.instructions.items.len + 1);
   6093         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
   6094             sub_block_len);
   6095         try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
   6096         const sub_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   6097 
   6098         sema.air_instructions.items(.tag)[@intFromEnum(br)] = .block;
   6099         sema.air_instructions.items(.data)[@intFromEnum(br)] = .{ .ty_pl = .{
   6100             .ty = .noreturn_type,
   6101             .payload = sema.addExtraAssumeCapacity(Air.Block{
   6102                 .body_len = sub_block_len,
   6103             }),
   6104         } };
   6105         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items));
   6106         sema.air_extra.appendAssumeCapacity(@intFromEnum(sub_br_inst));
   6107 
   6108         sema.air_instructions.appendAssumeCapacity(.{
   6109             .tag = .br,
   6110             .data = .{ .br = .{
   6111                 .block_inst = merges.block_inst,
   6112                 .operand = coerced_operand,
   6113             } },
   6114         });
   6115     }
   6116 
   6117     if (try sema.typeHasOnePossibleValue(resolved_ty)) |block_only_value| {
   6118         return Air.internedToRef(block_only_value.toIntern());
   6119     }
   6120 
   6121     return merges.block_inst.toRef();
   6122 }
   6123 
   6124 fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6125     const tracy = trace(@src());
   6126     defer tracy.end();
   6127 
   6128     const pt = sema.pt;
   6129     const zcu = pt.zcu;
   6130     const ip = &zcu.intern_pool;
   6131     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   6132     const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
   6133 
   6134     const src = block.nodeOffset(inst_data.src_node);
   6135     const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   6136     const options_src = block.builtinCallArgSrc(inst_data.src_node, 1);
   6137 
   6138     const ptr = try sema.resolveInst(extra.exported);
   6139     const ptr_val = try sema.resolveConstDefinedValue(block, ptr_src, ptr, .{ .simple = .export_target });
   6140     const ptr_ty = ptr_val.typeOf(zcu);
   6141 
   6142     const options = try sema.resolveExportOptions(block, options_src, extra.options);
   6143 
   6144     {
   6145         if (ptr_ty.zigTypeTag(zcu) != .pointer) {
   6146             return sema.fail(block, ptr_src, "expected pointer type, found '{f}'", .{ptr_ty.fmt(pt)});
   6147         }
   6148         const ptr_ty_info = ptr_ty.ptrInfo(zcu);
   6149         if (ptr_ty_info.flags.size == .slice) {
   6150             return sema.fail(block, ptr_src, "export target cannot be slice", .{});
   6151         }
   6152         if (ptr_ty_info.packed_offset.host_size != 0) {
   6153             return sema.fail(block, ptr_src, "export target cannot be bit-pointer", .{});
   6154         }
   6155     }
   6156 
   6157     const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr;
   6158     switch (ptr_info.base_addr) {
   6159         .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}),
   6160         .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}),
   6161         .uav => |uav| {
   6162             if (ptr_info.byte_offset != 0) {
   6163                 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
   6164             }
   6165             if (options.linkage == .internal) return;
   6166             const export_ty = Value.fromInterned(uav.val).typeOf(zcu);
   6167             if (!try sema.validateExternType(export_ty, .other)) {
   6168                 return sema.failWithOwnedErrorMsg(block, msg: {
   6169                     const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)});
   6170                     errdefer msg.destroy(sema.gpa);
   6171                     try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other);
   6172                     try sema.addDeclaredHereNote(msg, export_ty);
   6173                     break :msg msg;
   6174                 });
   6175             }
   6176             try sema.exports.append(zcu.gpa, .{
   6177                 .opts = options,
   6178                 .src = src,
   6179                 .exported = .{ .uav = uav.val },
   6180                 .status = .in_progress,
   6181             });
   6182         },
   6183         .nav => |nav| {
   6184             if (ptr_info.byte_offset != 0) {
   6185                 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
   6186             }
   6187             try sema.analyzeExport(block, src, options, nav);
   6188         },
   6189     }
   6190 }
   6191 
   6192 pub fn analyzeExport(
   6193     sema: *Sema,
   6194     block: *Block,
   6195     src: LazySrcLoc,
   6196     options: Zcu.Export.Options,
   6197     orig_nav_index: InternPool.Nav.Index,
   6198 ) !void {
   6199     const gpa = sema.gpa;
   6200     const pt = sema.pt;
   6201     const zcu = pt.zcu;
   6202     const ip = &zcu.intern_pool;
   6203 
   6204     if (options.linkage == .internal)
   6205         return;
   6206 
   6207     try sema.ensureNavResolved(block, src, orig_nav_index, .fully);
   6208 
   6209     const exported_nav_index = switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
   6210         .variable => |v| v.owner_nav,
   6211         .@"extern" => |e| e.owner_nav,
   6212         .func => |f| f.owner_nav,
   6213         else => orig_nav_index,
   6214     };
   6215 
   6216     const exported_nav = ip.getNav(exported_nav_index);
   6217     const export_ty: Type = .fromInterned(exported_nav.typeOf(ip));
   6218 
   6219     if (!try sema.validateExternType(export_ty, .other)) {
   6220         return sema.failWithOwnedErrorMsg(block, msg: {
   6221             const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)});
   6222             errdefer msg.destroy(gpa);
   6223 
   6224             try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other);
   6225 
   6226             try sema.addDeclaredHereNote(msg, export_ty);
   6227             break :msg msg;
   6228         });
   6229     }
   6230 
   6231     // TODO: some backends might support re-exporting extern decls
   6232     if (exported_nav.getExtern(ip) != null) {
   6233         return sema.fail(block, src, "export target cannot be extern", .{});
   6234     }
   6235 
   6236     try sema.maybeQueueFuncBodyAnalysis(block, src, exported_nav_index);
   6237 
   6238     try sema.exports.append(gpa, .{
   6239         .opts = options,
   6240         .src = src,
   6241         .exported = .{ .nav = exported_nav_index },
   6242         .status = .in_progress,
   6243     });
   6244 }
   6245 
   6246 fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
   6247     const pt = sema.pt;
   6248     const zcu = pt.zcu;
   6249     const ip = &zcu.intern_pool;
   6250     const func = switch (sema.owner.unwrap()) {
   6251         .func => |func| func,
   6252         .@"comptime",
   6253         .nav_val,
   6254         .nav_ty,
   6255         .type,
   6256         .memoized_state,
   6257         => return, // does nothing outside a function
   6258     };
   6259     ip.funcSetDisableInstrumentation(func);
   6260     sema.allow_memoize = false;
   6261 }
   6262 
   6263 fn zirDisableIntrinsics(sema: *Sema) CompileError!void {
   6264     const pt = sema.pt;
   6265     const zcu = pt.zcu;
   6266     const ip = &zcu.intern_pool;
   6267     const func = switch (sema.owner.unwrap()) {
   6268         .func => |func| func,
   6269         .@"comptime",
   6270         .nav_val,
   6271         .nav_ty,
   6272         .type,
   6273         .memoized_state,
   6274         => return, // does nothing outside a function
   6275     };
   6276     ip.funcSetDisableIntrinsics(func);
   6277     sema.allow_memoize = false;
   6278 }
   6279 
   6280 fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
   6281     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   6282     const src = block.builtinCallArgSrc(extra.node, 0);
   6283     block.float_mode = try sema.resolveBuiltinEnum(block, src, extra.operand, .FloatMode, .{ .simple = .operand_setFloatMode });
   6284 }
   6285 
   6286 fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6287     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   6288     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   6289     block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, .{ .simple = .operand_setRuntimeSafety });
   6290 }
   6291 
   6292 fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6293     const tracy = trace(@src());
   6294     defer tracy.end();
   6295 
   6296     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break";
   6297     const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
   6298     const operand = try sema.resolveInst(inst_data.operand);
   6299     const zir_block = extra.block_inst;
   6300 
   6301     var block = start_block;
   6302     while (true) {
   6303         if (block.label) |label| {
   6304             if (label.zir_block == zir_block) {
   6305                 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
   6306                 const src_loc = if (extra.operand_src_node.unwrap()) |operand_src_node|
   6307                     start_block.nodeOffset(operand_src_node)
   6308                 else
   6309                     null;
   6310                 try label.merges.src_locs.append(sema.gpa, src_loc);
   6311                 try label.merges.results.append(sema.gpa, operand);
   6312                 try label.merges.br_list.append(sema.gpa, br_ref.toIndex().?);
   6313                 block.runtime_index.increment();
   6314                 if (block.runtime_cond == null and block.runtime_loop == null) {
   6315                     block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
   6316                     block.runtime_loop = start_block.runtime_loop;
   6317                 }
   6318                 return;
   6319             }
   6320         }
   6321         block = block.parent.?;
   6322     }
   6323 }
   6324 
   6325 fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6326     const tracy = trace(@src());
   6327     defer tracy.end();
   6328 
   6329     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break";
   6330     const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
   6331     const operand_src = start_block.nodeOffset(extra.operand_src_node.unwrap().?);
   6332     const uncoerced_operand = try sema.resolveInst(inst_data.operand);
   6333     const switch_inst = extra.block_inst;
   6334 
   6335     switch (sema.code.instructions.items(.tag)[@intFromEnum(switch_inst)]) {
   6336         .switch_block, .switch_block_ref => {},
   6337         else => unreachable, // assertion failure
   6338     }
   6339 
   6340     const switch_payload_index = sema.code.instructions.items(.data)[@intFromEnum(switch_inst)].pl_node.payload_index;
   6341     const switch_operand_ref = sema.code.extraData(Zir.Inst.SwitchBlock, switch_payload_index).data.operand;
   6342     const switch_operand_ty = sema.typeOf(try sema.resolveInst(switch_operand_ref));
   6343 
   6344     const operand = try sema.coerce(start_block, switch_operand_ty, uncoerced_operand, operand_src);
   6345 
   6346     try sema.validateRuntimeValue(start_block, operand_src, operand);
   6347 
   6348     // We want to generate a `switch_dispatch` instruction with the switch condition,
   6349     // possibly preceded by a store to the stack alloc containing the raw operand.
   6350     // However, to avoid too much special-case state in Sema, this is handled by the
   6351     // `switch` lowering logic. As such, we will find the `Block` corresponding to the
   6352     // parent `switch_block[_ref]` instruction, create a dummy `br`, and add a merge
   6353     // to signal to the switch logic to rewrite this into an appropriate dispatch.
   6354 
   6355     var block = start_block;
   6356     while (true) {
   6357         if (block.label) |label| {
   6358             if (label.zir_block == switch_inst) {
   6359                 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
   6360                 try label.merges.extra_insts.append(sema.gpa, br_ref.toIndex().?);
   6361                 try label.merges.extra_src_locs.append(sema.gpa, operand_src);
   6362                 block.runtime_index.increment();
   6363                 if (block.runtime_cond == null and block.runtime_loop == null) {
   6364                     block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
   6365                     block.runtime_loop = start_block.runtime_loop;
   6366                 }
   6367                 return;
   6368             }
   6369         }
   6370         block = block.parent.?;
   6371     }
   6372 }
   6373 
   6374 fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
   6375     if (block.isComptime() or block.ownerModule().strip) return;
   6376 
   6377     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
   6378 
   6379     if (block.instructions.items.len != 0) {
   6380         const idx = block.instructions.items[block.instructions.items.len - 1];
   6381         if (sema.air_instructions.items(.tag)[@intFromEnum(idx)] == .dbg_stmt) {
   6382             // The previous dbg_stmt didn't correspond to any actual code, so replace it.
   6383             sema.air_instructions.items(.data)[@intFromEnum(idx)].dbg_stmt = .{
   6384                 .line = inst_data.line,
   6385                 .column = inst_data.column,
   6386             };
   6387             return;
   6388         }
   6389     }
   6390 
   6391     _ = try block.addInst(.{
   6392         .tag = .dbg_stmt,
   6393         .data = .{ .dbg_stmt = .{
   6394             .line = inst_data.line,
   6395             .column = inst_data.column,
   6396         } },
   6397     });
   6398 }
   6399 
   6400 fn zirDbgEmptyStmt(_: *Sema, block: *Block, _: Zir.Inst.Index) CompileError!void {
   6401     if (block.isComptime() or block.ownerModule().strip) return;
   6402     _ = try block.addNoOp(.dbg_empty_stmt);
   6403 }
   6404 
   6405 fn zirDbgVar(
   6406     sema: *Sema,
   6407     block: *Block,
   6408     inst: Zir.Inst.Index,
   6409     air_tag: Air.Inst.Tag,
   6410 ) CompileError!void {
   6411     const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op;
   6412     const operand = try sema.resolveInst(str_op.operand);
   6413     const name = str_op.getStr(sema.code);
   6414     try sema.addDbgVar(block, operand, air_tag, name);
   6415 }
   6416 
   6417 fn addDbgVar(
   6418     sema: *Sema,
   6419     block: *Block,
   6420     operand: Air.Inst.Ref,
   6421     air_tag: Air.Inst.Tag,
   6422     name: []const u8,
   6423 ) CompileError!void {
   6424     if (block.isComptime() or block.ownerModule().strip) return;
   6425 
   6426     const pt = sema.pt;
   6427     const zcu = pt.zcu;
   6428     const operand_ty = sema.typeOf(operand);
   6429     const val_ty = switch (air_tag) {
   6430         .dbg_var_ptr => operand_ty.childType(zcu),
   6431         .dbg_var_val, .dbg_arg_inline => operand_ty,
   6432         else => unreachable,
   6433     };
   6434     if (try val_ty.comptimeOnlySema(pt)) return;
   6435     if (!(try val_ty.hasRuntimeBitsSema(pt))) return;
   6436     if (try sema.resolveValue(operand)) |operand_val| {
   6437         if (operand_val.canMutateComptimeVarState(zcu)) return;
   6438     }
   6439 
   6440     // To ensure the lexical scoping is known to backends, this alloc must be
   6441     // within a real runtime block. We set a flag which communicates information
   6442     // to the closest lexically enclosing block:
   6443     // * If it is a `block_inline`, communicates to logic in `analyzeBodyInner`
   6444     //   to create a post-hoc block.
   6445     // * Otherwise, communicates to logic in `resolveBlockBody` to create a
   6446     //   real `block` instruction.
   6447     if (block.need_debug_scope) |ptr| ptr.* = true;
   6448 
   6449     // Add the name to the AIR.
   6450     const name_nts = try sema.appendAirString(name);
   6451 
   6452     _ = try block.addInst(.{
   6453         .tag = air_tag,
   6454         .data = .{ .pl_op = .{
   6455             .payload = @intFromEnum(name_nts),
   6456             .operand = operand,
   6457         } },
   6458     });
   6459 }
   6460 
   6461 pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTerminatedString {
   6462     if (str.len == 0) return .none;
   6463     const nts: Air.NullTerminatedString = @enumFromInt(sema.air_extra.items.len);
   6464     const elements_used = str.len / 4 + 1;
   6465     const elements = try sema.air_extra.addManyAsSlice(sema.gpa, elements_used);
   6466     const buffer = mem.sliceAsBytes(elements);
   6467     @memcpy(buffer[0..str.len], str);
   6468     buffer[str.len] = 0;
   6469     return nts;
   6470 }
   6471 
   6472 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   6473     const pt = sema.pt;
   6474     const zcu = pt.zcu;
   6475     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   6476     const src = block.tokenOffset(inst_data.src_tok);
   6477     const decl_name = try zcu.intern_pool.getOrPutString(
   6478         sema.gpa,
   6479         pt.tid,
   6480         inst_data.get(sema.code),
   6481         .no_embedded_nulls,
   6482     );
   6483     const nav_index = try sema.lookupIdentifier(block, decl_name);
   6484     return sema.analyzeNavRef(block, src, nav_index);
   6485 }
   6486 
   6487 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   6488     const pt = sema.pt;
   6489     const zcu = pt.zcu;
   6490     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   6491     const src = block.tokenOffset(inst_data.src_tok);
   6492     const decl_name = try zcu.intern_pool.getOrPutString(
   6493         sema.gpa,
   6494         pt.tid,
   6495         inst_data.get(sema.code),
   6496         .no_embedded_nulls,
   6497     );
   6498     const nav = try sema.lookupIdentifier(block, decl_name);
   6499     return sema.analyzeNavVal(block, src, nav);
   6500 }
   6501 
   6502 fn lookupIdentifier(sema: *Sema, block: *Block, name: InternPool.NullTerminatedString) !InternPool.Nav.Index {
   6503     const pt = sema.pt;
   6504     const zcu = pt.zcu;
   6505     var namespace = block.namespace;
   6506     while (true) {
   6507         if (try sema.lookupInNamespace(block, namespace, name)) |lookup| {
   6508             assert(lookup.accessible);
   6509             return lookup.nav;
   6510         }
   6511         namespace = zcu.namespacePtr(namespace).parent.unwrap() orelse break;
   6512     }
   6513     unreachable; // AstGen detects use of undeclared identifiers.
   6514 }
   6515 
   6516 /// This looks up a member of a specific namespace.
   6517 fn lookupInNamespace(
   6518     sema: *Sema,
   6519     block: *Block,
   6520     namespace_index: InternPool.NamespaceIndex,
   6521     ident_name: InternPool.NullTerminatedString,
   6522 ) CompileError!?struct {
   6523     nav: InternPool.Nav.Index,
   6524     /// If `false`, the declaration is in a different file and is not `pub`.
   6525     /// We still return the declaration for better error reporting.
   6526     accessible: bool,
   6527 } {
   6528     const pt = sema.pt;
   6529     const zcu = pt.zcu;
   6530 
   6531     try pt.ensureNamespaceUpToDate(namespace_index);
   6532 
   6533     const namespace = zcu.namespacePtr(namespace_index);
   6534 
   6535     const adapter: Zcu.Namespace.NameAdapter = .{ .zcu = zcu };
   6536 
   6537     const src_file = zcu.namespacePtr(block.namespace).file_scope;
   6538 
   6539     if (Type.fromInterned(namespace.owner_type).typeDeclInst(zcu)) |type_decl_inst| {
   6540         try sema.declareDependency(.{ .namespace_name = .{
   6541             .namespace = type_decl_inst,
   6542             .name = ident_name,
   6543         } });
   6544     }
   6545 
   6546     if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
   6547         return .{
   6548             .nav = nav_index,
   6549             .accessible = true,
   6550         };
   6551     } else if (namespace.priv_decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
   6552         return .{
   6553             .nav = nav_index,
   6554             .accessible = src_file == namespace.file_scope,
   6555         };
   6556     }
   6557 
   6558     return null;
   6559 }
   6560 
   6561 fn funcDeclSrcInst(sema: *Sema, func_inst: Air.Inst.Ref) !?InternPool.TrackedInst.Index {
   6562     const pt = sema.pt;
   6563     const zcu = pt.zcu;
   6564     const ip = &zcu.intern_pool;
   6565     const func_val = try sema.resolveValue(func_inst) orelse return null;
   6566     if (func_val.isUndef(zcu)) return null;
   6567     const nav = switch (ip.indexToKey(func_val.toIntern())) {
   6568         .@"extern" => |e| e.owner_nav,
   6569         .func => |f| f.owner_nav,
   6570         .ptr => |ptr| switch (ptr.base_addr) {
   6571             .nav => |nav| if (ptr.byte_offset == 0) nav else return null,
   6572             else => return null,
   6573         },
   6574         else => return null,
   6575     };
   6576     return ip.getNav(nav).srcInst(ip);
   6577 }
   6578 
   6579 pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref {
   6580     const pt = sema.pt;
   6581     const zcu = pt.zcu;
   6582     const gpa = sema.gpa;
   6583 
   6584     if (block.isComptime() or block.is_typeof) {
   6585         const index_val = try pt.intValue_u64(.usize, sema.comptime_err_ret_trace.items.len);
   6586         return Air.internedToRef(index_val.toIntern());
   6587     }
   6588 
   6589     if (!block.ownerModule().error_tracing) return .none;
   6590 
   6591     const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
   6592     try stack_trace_ty.resolveFields(pt);
   6593     const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
   6594     const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) {
   6595         error.AnalysisFail => @panic("std.builtin.StackTrace is corrupt"),
   6596         error.ComptimeReturn, error.ComptimeBreak => unreachable,
   6597         error.OutOfMemory => |e| return e,
   6598     };
   6599 
   6600     return try block.addInst(.{
   6601         .tag = .save_err_return_trace_index,
   6602         .data = .{ .ty_pl = .{
   6603             .ty = Air.internedToRef(stack_trace_ty.toIntern()),
   6604             .payload = @intCast(field_index),
   6605         } },
   6606     });
   6607 }
   6608 
   6609 /// Add instructions to block to "pop" the error return trace.
   6610 /// If `operand` is provided, only pops if operand is non-error.
   6611 fn popErrorReturnTrace(
   6612     sema: *Sema,
   6613     block: *Block,
   6614     src: LazySrcLoc,
   6615     operand: Air.Inst.Ref,
   6616     saved_error_trace_index: Air.Inst.Ref,
   6617 ) CompileError!void {
   6618     const pt = sema.pt;
   6619     const zcu = pt.zcu;
   6620     const gpa = sema.gpa;
   6621     var is_non_error: ?bool = null;
   6622     var is_non_error_inst: Air.Inst.Ref = undefined;
   6623     if (operand != .none) {
   6624         is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand);
   6625         if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val|
   6626             is_non_error = cond_val.toBool();
   6627     } else is_non_error = true; // no operand means pop unconditionally
   6628 
   6629     if (is_non_error == true) {
   6630         // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or
   6631         // the result is comptime-known to be a non-error. Either way, pop unconditionally.
   6632 
   6633         const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace);
   6634         try stack_trace_ty.resolveFields(pt);
   6635         const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
   6636         const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
   6637         const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
   6638         const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true);
   6639         try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store);
   6640     } else if (is_non_error == null) {
   6641         // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need
   6642         // to pop any error trace that may have been propagated from our arguments.
   6643 
   6644         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len);
   6645         const cond_block_inst = try block.addInstAsIndex(.{
   6646             .tag = .block,
   6647             .data = .{
   6648                 .ty_pl = .{
   6649                     .ty = .void_type,
   6650                     .payload = undefined, // updated below
   6651                 },
   6652             },
   6653         });
   6654 
   6655         var then_block = block.makeSubBlock();
   6656         defer then_block.instructions.deinit(gpa);
   6657 
   6658         // If non-error, then pop the error return trace by restoring the index.
   6659         const stack_trace_ty = try sema.getBuiltinType(src, .StackTrace);
   6660         try stack_trace_ty.resolveFields(pt);
   6661         const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
   6662         const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty);
   6663         const field_name = try zcu.intern_pool.getOrPutString(gpa, pt.tid, "index", .no_embedded_nulls);
   6664         const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true);
   6665         try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store);
   6666         _ = try then_block.addBr(cond_block_inst, .void_value);
   6667 
   6668         // Otherwise, do nothing
   6669         var else_block = block.makeSubBlock();
   6670         defer else_block.instructions.deinit(gpa);
   6671         _ = try else_block.addBr(cond_block_inst, .void_value);
   6672 
   6673         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
   6674             then_block.instructions.items.len + else_block.instructions.items.len +
   6675             @typeInfo(Air.Block).@"struct".fields.len + 1); // +1 for the sole .cond_br instruction in the .block
   6676 
   6677         const cond_br_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   6678         try sema.air_instructions.append(gpa, .{
   6679             .tag = .cond_br,
   6680             .data = .{
   6681                 .pl_op = .{
   6682                     .operand = is_non_error_inst,
   6683                     .payload = sema.addExtraAssumeCapacity(Air.CondBr{
   6684                         .then_body_len = @intCast(then_block.instructions.items.len),
   6685                         .else_body_len = @intCast(else_block.instructions.items.len),
   6686                         .branch_hints = .{
   6687                             // Weight against error branch.
   6688                             .true = .likely,
   6689                             .false = .unlikely,
   6690                             // Code coverage is not valuable on either branch.
   6691                             .then_cov = .none,
   6692                             .else_cov = .none,
   6693                         },
   6694                     }),
   6695                 },
   6696             },
   6697         });
   6698         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
   6699         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
   6700 
   6701         sema.air_instructions.items(.data)[@intFromEnum(cond_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 });
   6702         sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst));
   6703     }
   6704 }
   6705 
   6706 fn zirCall(
   6707     sema: *Sema,
   6708     block: *Block,
   6709     inst: Zir.Inst.Index,
   6710     comptime kind: enum { direct, field },
   6711 ) CompileError!Air.Inst.Ref {
   6712     const tracy = trace(@src());
   6713     defer tracy.end();
   6714 
   6715     const pt = sema.pt;
   6716     const zcu = pt.zcu;
   6717     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   6718     const callee_src = block.src(.{ .node_offset_call_func = inst_data.src_node });
   6719     const call_src = block.nodeOffset(inst_data.src_node);
   6720     const ExtraType = switch (kind) {
   6721         .direct => Zir.Inst.Call,
   6722         .field => Zir.Inst.FieldCall,
   6723     };
   6724     const extra = sema.code.extraData(ExtraType, inst_data.payload_index);
   6725     const args_len = extra.data.flags.args_len;
   6726 
   6727     const modifier: std.builtin.CallModifier = @enumFromInt(extra.data.flags.packed_modifier);
   6728     const ensure_result_used = extra.data.flags.ensure_result_used;
   6729     const pop_error_return_trace = extra.data.flags.pop_error_return_trace;
   6730 
   6731     const callee: ResolvedFieldCallee = switch (kind) {
   6732         .direct => .{ .direct = try sema.resolveInst(extra.data.callee) },
   6733         .field => blk: {
   6734             const object_ptr = try sema.resolveInst(extra.data.obj_ptr);
   6735             const field_name = try zcu.intern_pool.getOrPutString(
   6736                 sema.gpa,
   6737                 pt.tid,
   6738                 sema.code.nullTerminatedString(extra.data.field_name_start),
   6739                 .no_embedded_nulls,
   6740             );
   6741             const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
   6742             break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src);
   6743         },
   6744     };
   6745     const func: Air.Inst.Ref = switch (callee) {
   6746         .direct => |func_inst| func_inst,
   6747         .method => |method| method.func_inst,
   6748     };
   6749 
   6750     const callee_ty = sema.typeOf(func);
   6751     const total_args = args_len + @intFromBool(callee == .method);
   6752     const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, callee == .method);
   6753 
   6754     // The block index before the call, so we can potentially insert an error trace save here later.
   6755     const block_index: Air.Inst.Index = @enumFromInt(block.instructions.items.len);
   6756 
   6757     // This will be set by `analyzeCall` to indicate whether any parameter was an error (making the
   6758     // error trace potentially dirty).
   6759     var input_is_error = false;
   6760 
   6761     const args_info: CallArgsInfo = .{ .zir_call = .{
   6762         .bound_arg = switch (callee) {
   6763             .direct => .none,
   6764             .method => |method| method.arg0_inst,
   6765         },
   6766         .bound_arg_src = callee_src,
   6767         .call_inst = inst,
   6768         .call_node_offset = inst_data.src_node,
   6769         .num_args = args_len,
   6770         .args_body = @ptrCast(sema.code.extra[extra.end..]),
   6771         .any_arg_is_error = &input_is_error,
   6772     } };
   6773 
   6774     // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction.
   6775     const call_dbg_node: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
   6776     const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
   6777 
   6778     if (block.ownerModule().error_tracing and
   6779         !block.isComptime() and !block.is_typeof and (input_is_error or pop_error_return_trace))
   6780     {
   6781         const return_ty = sema.typeOf(call_inst);
   6782         if (modifier != .always_tail and return_ty.isNoReturn(zcu))
   6783             return call_inst; // call to "fn (...) noreturn", don't pop
   6784 
   6785         // TODO: we don't fix up the error trace for always_tail correctly, we should be doing it
   6786         // *before* the recursive call. This will be a bit tricky to do and probably requires
   6787         // moving this logic into analyzeCall. But that's probably a good idea anyway.
   6788         if (modifier == .always_tail)
   6789             return call_inst;
   6790 
   6791         // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only
   6792         // need to clean-up our own trace if we were passed to a non-error-handling expression.
   6793         if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) {
   6794             const stack_trace_ty = try sema.getBuiltinType(call_src, .StackTrace);
   6795             try stack_trace_ty.resolveFields(pt);
   6796             const field_name = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, "index", .no_embedded_nulls);
   6797             const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src);
   6798 
   6799             // Insert a save instruction before the arg resolution + call instructions we just generated
   6800             const save_inst = try block.insertInst(block_index, .{
   6801                 .tag = .save_err_return_trace_index,
   6802                 .data = .{ .ty_pl = .{
   6803                     .ty = Air.internedToRef(stack_trace_ty.toIntern()),
   6804                     .payload = @intCast(field_index),
   6805                 } },
   6806             });
   6807 
   6808             // Pop the error return trace, testing the result for non-error if necessary
   6809             const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst;
   6810             try sema.popErrorReturnTrace(block, call_src, operand, save_inst);
   6811         }
   6812 
   6813         return call_inst;
   6814     } else {
   6815         return call_inst;
   6816     }
   6817 }
   6818 
   6819 fn checkCallArgumentCount(
   6820     sema: *Sema,
   6821     block: *Block,
   6822     func: Air.Inst.Ref,
   6823     func_src: LazySrcLoc,
   6824     callee_ty: Type,
   6825     total_args: usize,
   6826     member_fn: bool,
   6827 ) !Type {
   6828     const pt = sema.pt;
   6829     const zcu = pt.zcu;
   6830     const func_ty: Type = func_ty: {
   6831         switch (callee_ty.zigTypeTag(zcu)) {
   6832             .@"fn" => break :func_ty callee_ty,
   6833             .pointer => {
   6834                 const ptr_info = callee_ty.ptrInfo(zcu);
   6835                 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") {
   6836                     break :func_ty .fromInterned(ptr_info.child);
   6837                 }
   6838             },
   6839             .optional => {
   6840                 const opt_child = callee_ty.optionalChild(zcu);
   6841                 if (opt_child.zigTypeTag(zcu) == .@"fn" or (opt_child.isSinglePointer(zcu) and
   6842                     opt_child.childType(zcu).zigTypeTag(zcu) == .@"fn"))
   6843                 {
   6844                     const msg = msg: {
   6845                         const msg = try sema.errMsg(func_src, "cannot call optional type '{f}'", .{
   6846                             callee_ty.fmt(pt),
   6847                         });
   6848                         errdefer msg.destroy(sema.gpa);
   6849                         try sema.errNote(func_src, msg, "consider using '.?', 'orelse' or 'if'", .{});
   6850                         break :msg msg;
   6851                     };
   6852                     return sema.failWithOwnedErrorMsg(block, msg);
   6853                 }
   6854             },
   6855             else => {},
   6856         }
   6857         return sema.fail(block, func_src, "type '{f}' not a function", .{callee_ty.fmt(pt)});
   6858     };
   6859 
   6860     const func_ty_info = zcu.typeToFunc(func_ty).?;
   6861     const fn_params_len = func_ty_info.param_types.len;
   6862     const args_len = total_args - @intFromBool(member_fn);
   6863     if (func_ty_info.is_var_args) {
   6864         assert(callConvSupportsVarArgs(func_ty_info.cc));
   6865         if (total_args >= fn_params_len) return func_ty;
   6866     } else if (fn_params_len == total_args) {
   6867         return func_ty;
   6868     }
   6869 
   6870     const maybe_func_inst = try sema.funcDeclSrcInst(func);
   6871     const member_str = if (member_fn) "member function " else "";
   6872     const variadic_str = if (func_ty_info.is_var_args) "at least " else "";
   6873     const msg = msg: {
   6874         const msg = try sema.errMsg(
   6875             func_src,
   6876             "{s}expected {s}{d} argument(s), found {d}",
   6877             .{
   6878                 member_str,
   6879                 variadic_str,
   6880                 fn_params_len - @intFromBool(member_fn),
   6881                 args_len,
   6882             },
   6883         );
   6884         errdefer msg.destroy(sema.gpa);
   6885 
   6886         if (maybe_func_inst) |func_inst| {
   6887             try sema.errNote(.{
   6888                 .base_node_inst = func_inst,
   6889                 .offset = LazySrcLoc.Offset.nodeOffset(.zero),
   6890             }, msg, "function declared here", .{});
   6891         }
   6892         break :msg msg;
   6893     };
   6894     return sema.failWithOwnedErrorMsg(block, msg);
   6895 }
   6896 
   6897 fn callBuiltin(
   6898     sema: *Sema,
   6899     block: *Block,
   6900     call_src: LazySrcLoc,
   6901     builtin_fn: Air.Inst.Ref,
   6902     modifier: std.builtin.CallModifier,
   6903     args: []const Air.Inst.Ref,
   6904     operation: CallOperation,
   6905 ) !void {
   6906     const pt = sema.pt;
   6907     const zcu = pt.zcu;
   6908     const callee_ty = sema.typeOf(builtin_fn);
   6909     const func_ty: Type = func_ty: {
   6910         switch (callee_ty.zigTypeTag(zcu)) {
   6911             .@"fn" => break :func_ty callee_ty,
   6912             .pointer => {
   6913                 const ptr_info = callee_ty.ptrInfo(zcu);
   6914                 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") {
   6915                     break :func_ty .fromInterned(ptr_info.child);
   6916                 }
   6917             },
   6918             else => {},
   6919         }
   6920         std.debug.panic("type '{f}' is not a function calling builtin fn", .{callee_ty.fmt(pt)});
   6921     };
   6922 
   6923     const func_ty_info = zcu.typeToFunc(func_ty).?;
   6924     const fn_params_len = func_ty_info.param_types.len;
   6925     if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) {
   6926         std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len });
   6927     }
   6928 
   6929     _ = try sema.analyzeCall(
   6930         block,
   6931         builtin_fn,
   6932         func_ty,
   6933         call_src,
   6934         call_src,
   6935         modifier,
   6936         false,
   6937         .{ .resolved = .{ .src = call_src, .args = args } },
   6938         null,
   6939         operation,
   6940     );
   6941 }
   6942 
   6943 const CallOperation = enum {
   6944     call,
   6945     @"@call",
   6946     @"@panic",
   6947     @"safety check",
   6948     @"error return",
   6949 };
   6950 
   6951 const CallArgsInfo = union(enum) {
   6952     /// The full list of resolved (but uncoerced) arguments is known ahead of time.
   6953     resolved: struct {
   6954         src: LazySrcLoc,
   6955         args: []const Air.Inst.Ref,
   6956     },
   6957 
   6958     /// The list of resolved (but uncoerced) arguments is known ahead of time, but
   6959     /// originated from a usage of the @call builtin at the given node offset.
   6960     call_builtin: struct {
   6961         call_node_offset: std.zig.Ast.Node.Offset,
   6962         args: []const Air.Inst.Ref,
   6963     },
   6964 
   6965     /// This call corresponds to a ZIR call instruction. The arguments have not yet been
   6966     /// resolved. They must be resolved by `analyzeCall` so that argument resolution and
   6967     /// generic instantiation may be interleaved. This is required for RLS to work on
   6968     /// generic parameters.
   6969     zir_call: struct {
   6970         /// This may be `none`, in which case it is ignored. Otherwise, it is the
   6971         /// already-resolved value of the first argument, from method call syntax.
   6972         bound_arg: Air.Inst.Ref,
   6973         /// The source location of `bound_arg` if it is not `null`. Otherwise `undefined`.
   6974         bound_arg_src: LazySrcLoc,
   6975         /// The ZIR call instruction. The parameter type is placed at this index while
   6976         /// analyzing arguments.
   6977         call_inst: Zir.Inst.Index,
   6978         /// The node offset of `call_inst`.
   6979         call_node_offset: std.zig.Ast.Node.Offset,
   6980         /// The number of arguments to this call, not including `bound_arg`.
   6981         num_args: u32,
   6982         /// The ZIR corresponding to all function arguments (other than `bound_arg`, if it
   6983         /// is not `none`). Format is precisely the same as trailing data of ZIR `call`.
   6984         args_body: []const Zir.Inst.Index,
   6985         /// This bool will be set to true if any argument evaluated turns out to have an error set or error union type.
   6986         /// This is used by the caller to restore the error return trace when necessary.
   6987         any_arg_is_error: *bool,
   6988     },
   6989 
   6990     fn count(cai: CallArgsInfo) usize {
   6991         return switch (cai) {
   6992             inline .resolved, .call_builtin => |resolved| resolved.args.len,
   6993             .zir_call => |zir_call| zir_call.num_args + @intFromBool(zir_call.bound_arg != .none),
   6994         };
   6995     }
   6996 
   6997     fn argSrc(cai: CallArgsInfo, block: *Block, arg_index: usize) LazySrcLoc {
   6998         return switch (cai) {
   6999             .resolved => |resolved| resolved.src,
   7000             .call_builtin => |call_builtin| block.src(.{ .call_arg = .{
   7001                 .call_node_offset = call_builtin.call_node_offset,
   7002                 .arg_index = @intCast(arg_index),
   7003             } }),
   7004             .zir_call => |zir_call| if (arg_index == 0 and zir_call.bound_arg != .none) {
   7005                 return zir_call.bound_arg_src;
   7006             } else block.src(.{ .call_arg = .{
   7007                 .call_node_offset = zir_call.call_node_offset,
   7008                 .arg_index = @intCast(arg_index - @intFromBool(zir_call.bound_arg != .none)),
   7009             } }),
   7010         };
   7011     }
   7012 
   7013     /// Analyzes the arg at `arg_index` and coerces it to `param_ty`.
   7014     /// `param_ty` may be `generic_poison`. A value of `null` indicates a varargs parameter.
   7015     /// `func_ty_info` may be the type before instantiation, even if a generic instantiation is in progress.
   7016     /// Emits a compile error if the argument is not comptime-known despite either `block.isComptime()` or
   7017     /// the parameter being marked `comptime`.
   7018     fn analyzeArg(
   7019         cai: CallArgsInfo,
   7020         sema: *Sema,
   7021         block: *Block,
   7022         arg_index: usize,
   7023         maybe_param_ty: ?Type,
   7024         func_ty_info: InternPool.Key.FuncType,
   7025         func_inst: Air.Inst.Ref,
   7026         maybe_func_src_inst: ?InternPool.TrackedInst.Index,
   7027     ) CompileError!Air.Inst.Ref {
   7028         const pt = sema.pt;
   7029         const zcu = pt.zcu;
   7030         const param_count = func_ty_info.param_types.len;
   7031         const uncoerced_arg: Air.Inst.Ref = switch (cai) {
   7032             inline .resolved, .call_builtin => |resolved| resolved.args[arg_index],
   7033             .zir_call => |zir_call| arg_val: {
   7034                 const has_bound_arg = zir_call.bound_arg != .none;
   7035                 if (arg_index == 0 and has_bound_arg) {
   7036                     break :arg_val zir_call.bound_arg;
   7037                 }
   7038                 const real_arg_idx = arg_index - @intFromBool(has_bound_arg);
   7039 
   7040                 const arg_body = if (real_arg_idx == 0) blk: {
   7041                     const start = zir_call.num_args;
   7042                     const end = @intFromEnum(zir_call.args_body[0]);
   7043                     break :blk zir_call.args_body[start..end];
   7044                 } else blk: {
   7045                     const start = @intFromEnum(zir_call.args_body[real_arg_idx - 1]);
   7046                     const end = @intFromEnum(zir_call.args_body[real_arg_idx]);
   7047                     break :blk zir_call.args_body[start..end];
   7048                 };
   7049 
   7050                 // Generate args to comptime params in comptime block
   7051                 const parent_comptime = block.comptime_reason;
   7052                 defer block.comptime_reason = parent_comptime;
   7053                 // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx`
   7054                 if (std.math.cast(u5, arg_index)) |i| {
   7055                     if (i < param_count and func_ty_info.paramIsComptime(i)) {
   7056                         block.comptime_reason = .{
   7057                             .reason = .{
   7058                                 .src = cai.argSrc(block, arg_index),
   7059                                 .r = .{
   7060                                     .comptime_param = .{
   7061                                         .comptime_src = if (maybe_func_src_inst) |src_inst| .{
   7062                                             .base_node_inst = src_inst,
   7063                                             .offset = .{ .func_decl_param_comptime = @intCast(arg_index) },
   7064                                         } else unreachable, // should be non-null because the function is generic
   7065                                     },
   7066                                 },
   7067                             },
   7068                         };
   7069                     }
   7070                 }
   7071                 // Give the arg its result type
   7072                 const provide_param_ty: Type = maybe_param_ty orelse .generic_poison;
   7073                 sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(provide_param_ty.toIntern()));
   7074                 // Resolve the arg!
   7075                 const uncoerced_arg = try sema.resolveInlineBody(block, arg_body, zir_call.call_inst);
   7076 
   7077                 if (block.isComptime() and !try sema.isComptimeKnown(uncoerced_arg)) {
   7078                     return sema.failWithNeededComptime(block, cai.argSrc(block, arg_index), null);
   7079                 }
   7080 
   7081                 if (sema.typeOf(uncoerced_arg).zigTypeTag(zcu) == .noreturn) {
   7082                     // This terminates resolution of arguments. The caller should
   7083                     // propagate this.
   7084                     return uncoerced_arg;
   7085                 }
   7086 
   7087                 if (sema.typeOf(uncoerced_arg).isError(zcu)) {
   7088                     zir_call.any_arg_is_error.* = true;
   7089                 }
   7090 
   7091                 break :arg_val uncoerced_arg;
   7092             },
   7093         };
   7094         const param_ty = maybe_param_ty orelse {
   7095             return sema.coerceVarArgParam(block, uncoerced_arg, cai.argSrc(block, arg_index));
   7096         };
   7097         switch (param_ty.toIntern()) {
   7098             .generic_poison_type => return uncoerced_arg,
   7099             else => return sema.coerceExtra(
   7100                 block,
   7101                 param_ty,
   7102                 uncoerced_arg,
   7103                 cai.argSrc(block, arg_index),
   7104                 .{ .param_src = .{
   7105                     .func_inst = func_inst,
   7106                     .param_i = @intCast(arg_index),
   7107                 } },
   7108             ) catch |err| switch (err) {
   7109                 error.NotCoercible => unreachable,
   7110                 else => |e| return e,
   7111             },
   7112         }
   7113     }
   7114 };
   7115 
   7116 fn analyzeCall(
   7117     sema: *Sema,
   7118     block: *Block,
   7119     callee: Air.Inst.Ref,
   7120     func_ty: Type,
   7121     func_src: LazySrcLoc,
   7122     call_src: LazySrcLoc,
   7123     modifier: std.builtin.CallModifier,
   7124     ensure_result_used: bool,
   7125     args_info: CallArgsInfo,
   7126     call_dbg_node: ?Zir.Inst.Index,
   7127     operation: CallOperation,
   7128 ) CompileError!Air.Inst.Ref {
   7129     const pt = sema.pt;
   7130     const zcu = pt.zcu;
   7131     const gpa = zcu.gpa;
   7132     const ip = &zcu.intern_pool;
   7133     const arena = sema.arena;
   7134 
   7135     const maybe_func_inst = try sema.funcDeclSrcInst(callee);
   7136     const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{
   7137         .base_node_inst = fn_decl_inst,
   7138         .offset = .{ .node_offset_fn_type_ret_ty = .zero },
   7139     } else func_src;
   7140 
   7141     const func_ty_info = zcu.typeToFunc(func_ty).?;
   7142     if (!callConvIsCallable(func_ty_info.cc)) {
   7143         return sema.failWithOwnedErrorMsg(block, msg: {
   7144             const msg = try sema.errMsg(
   7145                 func_src,
   7146                 "unable to call function with calling convention '{s}'",
   7147                 .{@tagName(func_ty_info.cc)},
   7148             );
   7149             errdefer msg.destroy(gpa);
   7150             if (maybe_func_inst) |func_inst| try sema.errNote(.{
   7151                 .base_node_inst = func_inst,
   7152                 .offset = .nodeOffset(.zero),
   7153             }, msg, "function declared here", .{});
   7154             break :msg msg;
   7155         });
   7156     }
   7157 
   7158     // We need this value in a few code paths.
   7159     const callee_val = try sema.resolveDefinedValue(block, call_src, callee);
   7160     // If the callee is a comptime-known *non-extern* function, `func_val` is populated.
   7161     // If it is a comptime-known extern function, `func_is_extern` is set instead.
   7162     // If it is not comptime-known, neither is set.
   7163     const func_val: ?Value, const func_is_extern: bool = if (callee_val) |c| switch (ip.indexToKey(c.toIntern())) {
   7164         .func => .{ c, false },
   7165         .ptr => switch (try sema.pointerDerefExtra(block, func_src, c)) {
   7166             .runtime_load, .needed_well_defined, .out_of_bounds => .{ null, false },
   7167             .val => |pointee| switch (ip.indexToKey(pointee.toIntern())) {
   7168                 .func => .{ pointee, false },
   7169                 .@"extern" => .{ null, true },
   7170                 else => unreachable,
   7171             },
   7172         },
   7173         .@"extern" => .{ null, true },
   7174         else => unreachable,
   7175     } else .{ null, false };
   7176 
   7177     if (func_ty_info.is_generic and func_val == null) {
   7178         return sema.failWithNeededComptime(block, func_src, .{ .simple = .generic_call_target });
   7179     }
   7180 
   7181     const inline_requested = func_ty_info.cc == .@"inline" or modifier == .always_inline;
   7182 
   7183     // If the modifier is `.compile_time`, or if the return type is non-generic and comptime-only,
   7184     // then we need to enter a comptime scope *now* to make sure the args are comptime-eval'd.
   7185     const old_block_comptime_reason = block.comptime_reason;
   7186     defer block.comptime_reason = old_block_comptime_reason;
   7187     if (!block.isComptime()) {
   7188         if (modifier == .compile_time) {
   7189             block.comptime_reason = .{ .reason = .{
   7190                 .src = call_src,
   7191                 .r = .{ .simple = .comptime_call_modifier },
   7192             } };
   7193         } else if (!inline_requested and try Type.fromInterned(func_ty_info.return_type).comptimeOnlySema(pt)) {
   7194             block.comptime_reason = .{
   7195                 .reason = .{
   7196                     .src = call_src,
   7197                     .r = .{
   7198                         .comptime_only_ret_ty = .{
   7199                             .ty = .fromInterned(func_ty_info.return_type),
   7200                             .is_generic_inst = false,
   7201                             .ret_ty_src = func_ret_ty_src,
   7202                         },
   7203                     },
   7204                 },
   7205             };
   7206         }
   7207     }
   7208 
   7209     // This is whether we already know this to be an inline call.
   7210     // If so, then comptime-known arguments are propagated when evaluating generic parameter/return types.
   7211     // We might still learn that this call is inline *after* evaluating the generic return type.
   7212     const early_known_inline = inline_requested or block.isComptime();
   7213 
   7214     // These values are undefined if `func_val == null`.
   7215     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: {
   7216         const info = ip.indexToKey(f.toIntern()).func;
   7217         const nav = ip.getNav(info.owner_nav);
   7218         const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail;
   7219         const file = zcu.fileByIndex(resolved_func_inst.file);
   7220         const zir_info = file.zir.?.getFnInfo(resolved_func_inst.inst);
   7221         break :b .{ nav, file.zir.?, info.zir_body_inst, resolved_func_inst.inst, zir_info };
   7222     } else .{ undefined, undefined, undefined, undefined, undefined };
   7223 
   7224     // This is the `inst_map` used when evaluating generic parameters and return types.
   7225     var generic_inst_map: InstMap = .{};
   7226     defer generic_inst_map.deinit(gpa);
   7227     if (func_ty_info.is_generic) {
   7228         try generic_inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body);
   7229     }
   7230 
   7231     // This exists so that `generic_block` below can include a "called from here" note back to this
   7232     // call site when analyzing generic parameter/return types.
   7233     var generic_inlining: Block.Inlining = if (func_ty_info.is_generic) .{
   7234         .call_block = block,
   7235         .call_src = call_src,
   7236         .func = func_val.?.toIntern(),
   7237         .is_generic_instantiation = true, // this allows the following fields to be `undefined`
   7238         .has_comptime_args = undefined,
   7239         .comptime_result = undefined,
   7240         .merges = undefined,
   7241     } else undefined;
   7242 
   7243     // This is the block in which we evaluate generic function components: that is, generic parameter
   7244     // types and the generic return type. This must not be used if the function is not generic.
   7245     // `comptime_reason` is set as needed.
   7246     var generic_block: Block = if (func_ty_info.is_generic) .{
   7247         .parent = null,
   7248         .sema = sema,
   7249         .namespace = fn_nav.analysis.?.namespace,
   7250         .instructions = .{},
   7251         .inlining = &generic_inlining,
   7252         .src_base_inst = fn_nav.analysis.?.zir_index,
   7253         .type_name_ctx = fn_nav.fqn,
   7254     } else undefined;
   7255     defer if (func_ty_info.is_generic) generic_block.instructions.deinit(gpa);
   7256 
   7257     if (func_ty_info.is_generic) {
   7258         // We certainly depend on the generic owner's signature!
   7259         try sema.declareDependency(.{ .src_hash = fn_tracked_inst });
   7260     }
   7261 
   7262     const args = try arena.alloc(Air.Inst.Ref, args_info.count());
   7263     for (args, 0..) |*arg, arg_idx| {
   7264         const param_ty: ?Type = if (arg_idx < func_ty_info.param_types.len) ty: {
   7265             const raw = func_ty_info.param_types.get(ip)[arg_idx];
   7266             if (raw != .generic_poison_type) break :ty .fromInterned(raw);
   7267 
   7268             // We must discover the generic parameter type.
   7269             assert(func_ty_info.is_generic);
   7270             const param_inst_idx = fn_zir_info.param_body[arg_idx];
   7271             const param_inst = fn_zir.instructions.get(@intFromEnum(param_inst_idx));
   7272             switch (param_inst.tag) {
   7273                 .param_anytype, .param_anytype_comptime => break :ty .generic_poison,
   7274                 .param, .param_comptime => {},
   7275                 else => unreachable,
   7276             }
   7277 
   7278             // Evaluate the generic parameter type. We need to switch out `sema.code` and `sema.inst_map`, because
   7279             // the function definition may be in a different file to the call site.
   7280             const old_code = sema.code;
   7281             const old_inst_map = sema.inst_map;
   7282             defer {
   7283                 generic_inst_map = sema.inst_map;
   7284                 sema.code = old_code;
   7285                 sema.inst_map = old_inst_map;
   7286             }
   7287             sema.code = fn_zir;
   7288             sema.inst_map = generic_inst_map;
   7289 
   7290             const extra = sema.code.extraData(Zir.Inst.Param, param_inst.data.pl_tok.payload_index);
   7291             const param_src = generic_block.tokenOffset(param_inst.data.pl_tok.src_tok);
   7292             const body = sema.code.bodySlice(extra.end, extra.data.type.body_len);
   7293 
   7294             generic_block.comptime_reason = .{ .reason = .{
   7295                 .r = .{ .simple = .function_parameters },
   7296                 .src = param_src,
   7297             } };
   7298 
   7299             const ty_ref = try sema.resolveInlineBody(&generic_block, body, param_inst_idx);
   7300             const param_ty = try sema.analyzeAsType(&generic_block, param_src, ty_ref);
   7301 
   7302             if (!param_ty.isValidParamType(zcu)) {
   7303                 const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
   7304                 return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{
   7305                     opaque_str, param_ty.fmt(pt),
   7306                 });
   7307             }
   7308 
   7309             break :ty param_ty;
   7310         } else null; // vararg
   7311 
   7312         arg.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, callee, maybe_func_inst);
   7313         const arg_ty = sema.typeOf(arg.*);
   7314         if (arg_ty.zigTypeTag(zcu) == .noreturn) {
   7315             return arg.*; // terminate analysis here
   7316         }
   7317 
   7318         if (func_ty_info.is_generic) {
   7319             // We need to put the argument into `generic_inst_map` so that other parameters can refer to it.
   7320             const param_inst_idx = fn_zir_info.param_body[arg_idx];
   7321             const declared_comptime = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsComptime(i) else false;
   7322             const param_is_comptime = declared_comptime or try arg_ty.comptimeOnlySema(pt);
   7323             // We allow comptime-known arguments to propagate to generic types not only for comptime
   7324             // parameters, but if the call is known to be inline.
   7325             if (param_is_comptime or early_known_inline) {
   7326                 if (param_is_comptime and !try sema.isComptimeKnown(arg.*)) {
   7327                     assert(!declared_comptime); // `analyzeArg` handles this
   7328                     const arg_src = args_info.argSrc(block, arg_idx);
   7329                     const param_ty_src: LazySrcLoc = .{
   7330                         .base_node_inst = maybe_func_inst.?, // the function is generic
   7331                         .offset = .{ .func_decl_param_ty = @intCast(arg_idx) },
   7332                     };
   7333                     return sema.failWithNeededComptime(
   7334                         block,
   7335                         arg_src,
   7336                         .{ .comptime_only_param_ty = .{ .ty = arg_ty, .param_ty_src = param_ty_src } },
   7337                     );
   7338                 }
   7339                 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, arg.*);
   7340             } else {
   7341                 // We need a dummy instruction with this type. It doesn't actually need to be in any block,
   7342                 // since it will never be referenced at runtime!
   7343                 const dummy: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   7344                 try sema.air_instructions.append(gpa, .{ .tag = .alloc, .data = .{ .ty = arg_ty } });
   7345                 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, dummy.toRef());
   7346             }
   7347         }
   7348     }
   7349 
   7350     // This return type is never generic poison.
   7351     // However, if it has an IES, it is always associated with the callee value.
   7352     // This is not correct for inline calls (where it should be an ad-hoc IES), nor for generic
   7353     // calls (where it should be the IES of the instantiation). However, it's how we print this
   7354     // in error messages.
   7355     const resolved_ret_ty: Type = ret_ty: {
   7356         if (!func_ty_info.is_generic) break :ret_ty .fromInterned(func_ty_info.return_type);
   7357 
   7358         const maybe_poison_bare = if (fn_zir_info.inferred_error_set) maybe_poison: {
   7359             break :maybe_poison ip.errorUnionPayload(func_ty_info.return_type);
   7360         } else func_ty_info.return_type;
   7361 
   7362         if (maybe_poison_bare != .generic_poison_type) break :ret_ty .fromInterned(func_ty_info.return_type);
   7363 
   7364         // Evaluate the generic return type. As with generic parameters, we switch out `sema.code` and `sema.inst_map`.
   7365 
   7366         assert(func_ty_info.is_generic);
   7367 
   7368         const old_code = sema.code;
   7369         const old_inst_map = sema.inst_map;
   7370         defer {
   7371             generic_inst_map = sema.inst_map;
   7372             sema.code = old_code;
   7373             sema.inst_map = old_inst_map;
   7374         }
   7375         sema.code = fn_zir;
   7376         sema.inst_map = generic_inst_map;
   7377 
   7378         generic_block.comptime_reason = .{ .reason = .{
   7379             .r = .{ .simple = .function_ret_ty },
   7380             .src = func_ret_ty_src,
   7381         } };
   7382 
   7383         const bare_ty = if (fn_zir_info.ret_ty_ref != .none) bare: {
   7384             assert(fn_zir_info.ret_ty_body.len == 0);
   7385             break :bare try sema.resolveType(&generic_block, func_ret_ty_src, fn_zir_info.ret_ty_ref);
   7386         } else bare: {
   7387             assert(fn_zir_info.ret_ty_body.len != 0);
   7388             const ty_ref = try sema.resolveInlineBody(&generic_block, fn_zir_info.ret_ty_body, fn_zir_inst);
   7389             break :bare try sema.analyzeAsType(&generic_block, func_ret_ty_src, ty_ref);
   7390         };
   7391         assert(bare_ty.toIntern() != .generic_poison_type);
   7392 
   7393         const full_ty = if (fn_zir_info.inferred_error_set) full: {
   7394             try sema.validateErrorUnionPayloadType(block, bare_ty, func_ret_ty_src);
   7395             const set = ip.errorUnionSet(func_ty_info.return_type);
   7396             break :full try pt.errorUnionType(.fromInterned(set), bare_ty);
   7397         } else bare_ty;
   7398 
   7399         if (!full_ty.isValidReturnType(zcu)) {
   7400             const opaque_str = if (full_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
   7401             return sema.fail(block, func_ret_ty_src, "{s}return type '{f}' not allowed", .{
   7402                 opaque_str, full_ty.fmt(pt),
   7403             });
   7404         }
   7405 
   7406         break :ret_ty full_ty;
   7407     };
   7408 
   7409     // If we've discovered after evaluating arguments that a generic function instantiation is
   7410     // comptime-only, then we can mark the block as comptime *now*.
   7411     if (!inline_requested and !block.isComptime() and try resolved_ret_ty.comptimeOnlySema(pt)) {
   7412         block.comptime_reason = .{
   7413             .reason = .{
   7414                 .src = call_src,
   7415                 .r = .{
   7416                     .comptime_only_ret_ty = .{
   7417                         .ty = resolved_ret_ty,
   7418                         .is_generic_inst = true,
   7419                         .ret_ty_src = func_ret_ty_src,
   7420                     },
   7421                 },
   7422             },
   7423         };
   7424     }
   7425 
   7426     if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
   7427 
   7428     const is_inline_call = block.isComptime() or inline_requested;
   7429 
   7430     if (!is_inline_call) {
   7431         if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: {
   7432             const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)});
   7433             errdefer msg.destroy(gpa);
   7434             switch (operation) {
   7435                 .call, .@"@call", .@"@panic", .@"error return" => {},
   7436                 .@"safety check" => try sema.errNote(call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}),
   7437             }
   7438             break :msg msg;
   7439         });
   7440         if (func_ty_info.cc == .auto) {
   7441             switch (sema.owner.unwrap()) {
   7442                 .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
   7443                 .func => |owner_func| ip.funcSetHasErrorTrace(owner_func, true),
   7444             }
   7445         }
   7446         for (args, 0..) |arg, arg_idx| {
   7447             try sema.validateRuntimeValue(block, args_info.argSrc(block, arg_idx), arg);
   7448         }
   7449         const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: {
   7450             if (!func_ty_info.is_generic) break :func .{ callee, args };
   7451 
   7452             // Instantiate the generic function!
   7453 
   7454             // This may be an overestimate, but it's definitely sufficient.
   7455             const max_runtime_args = args_info.count() - @popCount(func_ty_info.comptime_bits);
   7456             var runtime_args: std.ArrayListUnmanaged(Air.Inst.Ref) = try .initCapacity(arena, max_runtime_args);
   7457             var runtime_param_tys: std.ArrayListUnmanaged(InternPool.Index) = try .initCapacity(arena, max_runtime_args);
   7458 
   7459             const comptime_args = try arena.alloc(InternPool.Index, args_info.count());
   7460 
   7461             var noalias_bits: u32 = 0;
   7462 
   7463             for (args, comptime_args, 0..) |arg, *comptime_arg, arg_idx| {
   7464                 const arg_ty = sema.typeOf(arg);
   7465 
   7466                 const is_comptime = c: {
   7467                     if (std.math.cast(u5, arg_idx)) |i| {
   7468                         if (func_ty_info.paramIsComptime(i)) {
   7469                             break :c true;
   7470                         }
   7471                     }
   7472                     break :c try arg_ty.comptimeOnlySema(pt);
   7473                 };
   7474                 const is_noalias = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsNoalias(i) else false;
   7475 
   7476                 if (is_comptime) {
   7477                     // We already emitted an error if the argument isn't comptime-known.
   7478                     comptime_arg.* = (try sema.resolveValue(arg)).?.toIntern();
   7479                 } else {
   7480                     comptime_arg.* = .none;
   7481                     if (is_noalias) {
   7482                         const runtime_idx = runtime_args.items.len;
   7483                         noalias_bits |= @as(u32, 1) << @intCast(runtime_idx);
   7484                     }
   7485                     runtime_args.appendAssumeCapacity(arg);
   7486                     runtime_param_tys.appendAssumeCapacity(arg_ty.toIntern());
   7487                 }
   7488             }
   7489 
   7490             const bare_ret_ty = if (fn_zir_info.inferred_error_set) t: {
   7491                 break :t resolved_ret_ty.errorUnionPayload(zcu);
   7492             } else resolved_ret_ty;
   7493 
   7494             // We now need to actually create the function instance.
   7495             const func_instance = try ip.getFuncInstance(gpa, pt.tid, .{
   7496                 .param_types = runtime_param_tys.items,
   7497                 .noalias_bits = noalias_bits,
   7498                 .bare_return_type = bare_ret_ty.toIntern(),
   7499                 .is_noinline = func_ty_info.is_noinline,
   7500                 .inferred_error_set = fn_zir_info.inferred_error_set,
   7501                 .generic_owner = func_val.?.toIntern(),
   7502                 .comptime_args = comptime_args,
   7503             });
   7504             if (zcu.comp.debugIncremental()) {
   7505                 const nav = ip.indexToKey(func_instance).func.owner_nav;
   7506                 const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav);
   7507                 if (!gop.found_existing) gop.value_ptr.* = zcu.generation;
   7508             }
   7509 
   7510             // This call is problematic as it breaks guarantees about order-independency of semantic analysis.
   7511             // These guarantees are necessary for incremental compilation and parallel semantic analysis.
   7512             // See: #22410
   7513             zcu.funcInfo(func_instance).maxBranchQuota(ip, sema.branch_quota);
   7514 
   7515             break :func .{ Air.internedToRef(func_instance), runtime_args.items };
   7516         };
   7517 
   7518         ref_func: {
   7519             const runtime_func_val = try sema.resolveValue(runtime_func) orelse break :ref_func;
   7520             if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func;
   7521             try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = runtime_func_val.toIntern() }));
   7522             try zcu.ensureFuncBodyAnalysisQueued(runtime_func_val.toIntern());
   7523         }
   7524 
   7525         const call_tag: Air.Inst.Tag = switch (modifier) {
   7526             .auto, .no_suspend => .call,
   7527             .never_tail => .call_never_tail,
   7528             .never_inline => .call_never_inline,
   7529             .always_tail => .call_always_tail,
   7530 
   7531             .always_inline,
   7532             .compile_time,
   7533             => unreachable,
   7534         };
   7535 
   7536         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".fields.len + runtime_args.len);
   7537         const maybe_opv = try block.addInst(.{
   7538             .tag = call_tag,
   7539             .data = .{ .pl_op = .{
   7540                 .operand = runtime_func,
   7541                 .payload = sema.addExtraAssumeCapacity(Air.Call{
   7542                     .args_len = @intCast(runtime_args.len),
   7543                 }),
   7544             } },
   7545         });
   7546         sema.appendRefsAssumeCapacity(runtime_args);
   7547 
   7548         if (ensure_result_used) {
   7549             try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src);
   7550         }
   7551 
   7552         if (call_tag == .call_always_tail) {
   7553             const func_or_ptr_ty = sema.typeOf(runtime_func);
   7554             const runtime_func_ty = switch (func_or_ptr_ty.zigTypeTag(zcu)) {
   7555                 .@"fn" => func_or_ptr_ty,
   7556                 .pointer => func_or_ptr_ty.childType(zcu),
   7557                 else => unreachable,
   7558             };
   7559             return sema.handleTailCall(block, call_src, runtime_func_ty, maybe_opv);
   7560         }
   7561 
   7562         if (ip.isNoReturn(resolved_ret_ty.toIntern())) {
   7563             const want_check = c: {
   7564                 if (!block.wantSafety()) break :c false;
   7565                 if (func_val != null) break :c false;
   7566                 break :c true;
   7567             };
   7568             if (want_check) {
   7569                 try sema.safetyPanic(block, call_src, .noreturn_returned);
   7570             } else {
   7571                 _ = try block.addNoOp(.unreach);
   7572             }
   7573             return .unreachable_value;
   7574         }
   7575 
   7576         const result: Air.Inst.Ref = if (try sema.typeHasOnePossibleValue(sema.typeOf(maybe_opv))) |opv|
   7577             .fromValue(opv)
   7578         else
   7579             maybe_opv;
   7580 
   7581         return result;
   7582     }
   7583 
   7584     // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`.
   7585 
   7586     if (func_ty_info.is_noinline and !block.isComptime()) {
   7587         return sema.fail(block, call_src, "inline call of noinline function", .{});
   7588     }
   7589 
   7590     const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline";
   7591     if (modifier == .never_inline) {
   7592         const msg, const fail_block = msg: {
   7593             const msg = try sema.errMsg(call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type});
   7594             errdefer msg.destroy(gpa);
   7595             const fail_block = if (block.isComptime()) b: {
   7596                 break :b try block.explainWhyBlockIsComptime(msg);
   7597             } else block;
   7598             break :msg .{ msg, fail_block };
   7599         };
   7600         return sema.failWithOwnedErrorMsg(fail_block, msg);
   7601     }
   7602     if (func_ty_info.is_var_args) {
   7603         const msg, const fail_block = msg: {
   7604             const msg = try sema.errMsg(call_src, "{s} call of variadic function", .{call_type});
   7605             errdefer msg.destroy(gpa);
   7606             const fail_block = if (block.isComptime()) b: {
   7607                 break :b try block.explainWhyBlockIsComptime(msg);
   7608             } else block;
   7609             break :msg .{ msg, fail_block };
   7610         };
   7611         return sema.failWithOwnedErrorMsg(fail_block, msg);
   7612     }
   7613     if (func_val == null) {
   7614         if (func_is_extern) {
   7615             const msg, const fail_block = msg: {
   7616                 const msg = try sema.errMsg(call_src, "{s} call of extern function", .{call_type});
   7617                 errdefer msg.destroy(gpa);
   7618                 const fail_block = if (block.isComptime()) b: {
   7619                     break :b try block.explainWhyBlockIsComptime(msg);
   7620                 } else block;
   7621                 break :msg .{ msg, fail_block };
   7622             };
   7623             return sema.failWithOwnedErrorMsg(fail_block, msg);
   7624         }
   7625         return sema.failWithNeededComptime(
   7626             block,
   7627             func_src,
   7628             if (block.isComptime()) null else .{ .simple = .inline_call_target },
   7629         );
   7630     }
   7631 
   7632     if (block.isComptime()) {
   7633         for (args, 0..) |arg, arg_idx| {
   7634             if (!try sema.isComptimeKnown(arg)) {
   7635                 const arg_src = args_info.argSrc(block, arg_idx);
   7636                 return sema.failWithNeededComptime(block, arg_src, null);
   7637             }
   7638         }
   7639     }
   7640 
   7641     // For an inline call, we depend on the source code of the whole function definition.
   7642     try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index });
   7643 
   7644     try sema.emitBackwardBranch(block, call_src);
   7645 
   7646     const want_memoize = m: {
   7647         // TODO: comptime call memoization is currently not supported under incremental compilation
   7648         // since dependencies are not marked on callers. If we want to keep this around (we should
   7649         // check that it's worthwhile first!), each memoized call needs an `AnalUnit`.
   7650         if (zcu.comp.incremental) break :m false;
   7651         if (!block.isComptime()) break :m false;
   7652         for (args) |a| {
   7653             const val = (try sema.resolveValue(a)).?;
   7654             if (val.canMutateComptimeVarState(zcu)) break :m false;
   7655         }
   7656         break :m true;
   7657     };
   7658     const memoized_arg_values: []const InternPool.Index = if (want_memoize) arg_vals: {
   7659         const vals = try sema.arena.alloc(InternPool.Index, args.len);
   7660         for (vals, args) |*v, a| v.* = (try sema.resolveValue(a)).?.toIntern();
   7661         break :arg_vals vals;
   7662     } else undefined;
   7663     if (want_memoize) memoize: {
   7664         const memoized_call_index = ip.getIfExists(.{
   7665             .memoized_call = .{
   7666                 .func = func_val.?.toIntern(),
   7667                 .arg_values = memoized_arg_values,
   7668                 .result = undefined, // ignored by hash+eql
   7669                 .branch_count = undefined, // ignored by hash+eql
   7670             },
   7671         }) orelse break :memoize;
   7672         const memoized_call = ip.indexToKey(memoized_call_index).memoized_call;
   7673         if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) {
   7674             // Let the call play out se we get the correct source location for the
   7675             // "evaluation exceeded X backwards branches" error.
   7676             break :memoize;
   7677         }
   7678         sema.branch_count += memoized_call.branch_count;
   7679         const result = Air.internedToRef(memoized_call.result);
   7680         if (ensure_result_used) {
   7681             try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
   7682         }
   7683         return result;
   7684     }
   7685 
   7686     var new_ies: InferredErrorSet = .{ .func = .none };
   7687 
   7688     const old_inst_map = sema.inst_map;
   7689     const old_code = sema.code;
   7690     const old_func_index = sema.func_index;
   7691     const old_fn_ret_ty = sema.fn_ret_ty;
   7692     const old_fn_ret_ty_ies = sema.fn_ret_ty_ies;
   7693     const old_error_return_trace_index_on_fn_entry = sema.error_return_trace_index_on_fn_entry;
   7694     defer {
   7695         sema.inst_map.deinit(gpa);
   7696         sema.inst_map = old_inst_map;
   7697         sema.code = old_code;
   7698         sema.func_index = old_func_index;
   7699         sema.fn_ret_ty = old_fn_ret_ty;
   7700         sema.fn_ret_ty_ies = old_fn_ret_ty_ies;
   7701         sema.error_return_trace_index_on_fn_entry = old_error_return_trace_index_on_fn_entry;
   7702     }
   7703     sema.inst_map = .{};
   7704     sema.code = fn_zir;
   7705     sema.func_index = func_val.?.toIntern();
   7706     sema.fn_ret_ty = if (fn_zir_info.inferred_error_set) try pt.errorUnionType(
   7707         .fromInterned(.adhoc_inferred_error_set_type),
   7708         resolved_ret_ty.errorUnionPayload(zcu),
   7709     ) else resolved_ret_ty;
   7710     sema.fn_ret_ty_ies = if (fn_zir_info.inferred_error_set) &new_ies else null;
   7711 
   7712     try sema.inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body);
   7713     for (args, 0..) |arg, arg_idx| {
   7714         sema.inst_map.putAssumeCapacityNoClobber(fn_zir_info.param_body[arg_idx], arg);
   7715     }
   7716 
   7717     const need_debug_scope = !block.isComptime() and !block.is_typeof and !block.ownerModule().strip;
   7718     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
   7719     try sema.air_instructions.append(gpa, .{
   7720         .tag = if (need_debug_scope) .dbg_inline_block else .block,
   7721         .data = undefined,
   7722     });
   7723 
   7724     var inlining: Block.Inlining = .{
   7725         .call_block = block,
   7726         .call_src = call_src,
   7727         .func = func_val.?.toIntern(),
   7728         .is_generic_instantiation = false,
   7729         .has_comptime_args = for (args) |a| {
   7730             if (try sema.isComptimeKnown(a)) break true;
   7731         } else false,
   7732         .comptime_result = undefined,
   7733         .merges = .{
   7734             .block_inst = block_inst,
   7735             .results = .empty,
   7736             .br_list = .empty,
   7737             .src_locs = .empty,
   7738         },
   7739     };
   7740     var child_block: Block = .{
   7741         .parent = null,
   7742         .sema = sema,
   7743         .namespace = fn_nav.analysis.?.namespace,
   7744         .instructions = .{},
   7745         .inlining = &inlining,
   7746         .is_typeof = block.is_typeof,
   7747         .comptime_reason = if (block.isComptime()) .inlining_parent else null,
   7748         .error_return_trace_index = block.error_return_trace_index,
   7749         .runtime_cond = block.runtime_cond,
   7750         .runtime_loop = block.runtime_loop,
   7751         .runtime_index = block.runtime_index,
   7752         .src_base_inst = fn_nav.analysis.?.zir_index,
   7753         .type_name_ctx = fn_nav.fqn,
   7754     };
   7755 
   7756     defer child_block.instructions.deinit(gpa);
   7757     defer inlining.merges.deinit(gpa);
   7758 
   7759     if (!inlining.has_comptime_args) {
   7760         var block_it = block;
   7761         while (block_it.inlining) |parent_inlining| {
   7762             if (!parent_inlining.is_generic_instantiation and
   7763                 !parent_inlining.has_comptime_args and
   7764                 parent_inlining.func == func_val.?.toIntern())
   7765             {
   7766                 return sema.fail(block, call_src, "inline call is recursive", .{});
   7767             }
   7768             block_it = parent_inlining.call_block;
   7769         }
   7770     }
   7771 
   7772     if (!block.isComptime() and !block.is_typeof) {
   7773         const zir_tags = sema.code.instructions.items(.tag);
   7774         const zir_datas = sema.code.instructions.items(.data);
   7775         for (fn_zir_info.param_body) |inst| switch (zir_tags[@intFromEnum(inst)]) {
   7776             .param, .param_comptime => {
   7777                 const extra = sema.code.extraData(Zir.Inst.Param, zir_datas[@intFromEnum(inst)].pl_tok.payload_index);
   7778                 const param_name = sema.code.nullTerminatedString(extra.data.name);
   7779                 const air_inst = sema.inst_map.get(inst).?;
   7780                 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name);
   7781             },
   7782             .param_anytype, .param_anytype_comptime => {
   7783                 const param_name = zir_datas[@intFromEnum(inst)].str_tok.get(sema.code);
   7784                 const air_inst = sema.inst_map.get(inst).?;
   7785                 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name);
   7786             },
   7787             else => {},
   7788         };
   7789     }
   7790 
   7791     child_block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block);
   7792     // Save the error trace as our first action in the function
   7793     // to match the behavior of runtime function calls.
   7794     const error_return_trace_index_on_parent_fn_entry = sema.error_return_trace_index_on_fn_entry;
   7795     sema.error_return_trace_index_on_fn_entry = child_block.error_return_trace_index;
   7796     defer sema.error_return_trace_index_on_fn_entry = error_return_trace_index_on_parent_fn_entry;
   7797 
   7798     // We temporarily set `allow_memoize` to `true` to track this comptime call.
   7799     // It is restored after the call finishes analysis, so that a caller may
   7800     // know whether an in-progress call (containing this call) may be memoized.
   7801     const old_allow_memoize = sema.allow_memoize;
   7802     defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize;
   7803     sema.allow_memoize = true;
   7804 
   7805     // Store the current eval branch count so we can find out how many eval branches
   7806     // the comptime call caused.
   7807     const old_branch_count = sema.branch_count;
   7808 
   7809     const result_raw: Air.Inst.Ref = result: {
   7810         sema.analyzeFnBody(&child_block, fn_zir_info.body) catch |err| switch (err) {
   7811             error.ComptimeReturn => break :result inlining.comptime_result,
   7812             else => |e| return e,
   7813         };
   7814         break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope);
   7815     };
   7816 
   7817     const maybe_opv: Air.Inst.Ref = if (try sema.resolveValue(result_raw)) |result_val| r: {
   7818         const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern());
   7819         break :r Air.internedToRef(val_resolved);
   7820     } else r: {
   7821         const resolved_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result_raw).toIntern());
   7822         if (resolved_ty == .none) break :r result_raw;
   7823         // TODO: mutate in place the previous instruction if possible
   7824         // rather than adding a bitcast instruction.
   7825         break :r try block.addBitCast(.fromInterned(resolved_ty), result_raw);
   7826     };
   7827 
   7828     if (block.isComptime()) {
   7829         const result_val = (try sema.resolveValue(maybe_opv)).?;
   7830         if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) {
   7831             _ = try pt.intern(.{ .memoized_call = .{
   7832                 .func = func_val.?.toIntern(),
   7833                 .arg_values = memoized_arg_values,
   7834                 .result = result_val.toIntern(),
   7835                 .branch_count = sema.branch_count - old_branch_count,
   7836             } });
   7837         }
   7838     }
   7839 
   7840     if (ensure_result_used) {
   7841         try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src);
   7842     }
   7843 
   7844     return maybe_opv;
   7845 }
   7846 
   7847 fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
   7848     const pt = sema.pt;
   7849     const zcu = pt.zcu;
   7850     const target = zcu.getTarget();
   7851     const backend = zcu.comp.getZigBackend();
   7852     if (!target_util.supportsTailCall(target, backend)) {
   7853         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", .{
   7854             @tagName(backend), @tagName(target.cpu.arch),
   7855         });
   7856     }
   7857     const owner_func_ty: Type = .fromInterned(zcu.funcInfo(sema.owner.unwrap().func).ty);
   7858     if (owner_func_ty.toIntern() != func_ty.toIntern()) {
   7859         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}'", .{
   7860             func_ty.fmt(pt), owner_func_ty.fmt(pt),
   7861         });
   7862     }
   7863     _ = try block.addUnOp(.ret, result);
   7864     return .unreachable_value;
   7865 }
   7866 
   7867 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7868     const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type;
   7869     const ty = try sema.pt.intType(int_type.signedness, int_type.bit_count);
   7870     return Air.internedToRef(ty.toIntern());
   7871 }
   7872 
   7873 fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7874     const tracy = trace(@src());
   7875     defer tracy.end();
   7876 
   7877     const pt = sema.pt;
   7878     const zcu = pt.zcu;
   7879     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   7880     const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
   7881     const child_type = try sema.resolveType(block, operand_src, inst_data.operand);
   7882     if (child_type.zigTypeTag(zcu) == .@"opaque") {
   7883         return sema.fail(block, operand_src, "opaque type '{f}' cannot be optional", .{child_type.fmt(pt)});
   7884     } else if (child_type.zigTypeTag(zcu) == .null) {
   7885         return sema.fail(block, operand_src, "type '{f}' cannot be optional", .{child_type.fmt(pt)});
   7886     }
   7887     const opt_type = try pt.optionalType(child_type.toIntern());
   7888 
   7889     return Air.internedToRef(opt_type.toIntern());
   7890 }
   7891 
   7892 fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7893     const pt = sema.pt;
   7894     const zcu = pt.zcu;
   7895     const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
   7896     const maybe_wrapped_indexable_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, bin.lhs) orelse return .generic_poison_type;
   7897     const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(zcu);
   7898     try indexable_ty.resolveFields(pt);
   7899     assert(indexable_ty.isIndexable(zcu)); // validated by a previous instruction
   7900     if (indexable_ty.zigTypeTag(zcu) == .@"struct") {
   7901         const elem_type = indexable_ty.fieldType(@intFromEnum(bin.rhs), zcu);
   7902         return Air.internedToRef(elem_type.toIntern());
   7903     } else {
   7904         const elem_type = indexable_ty.elemType2(zcu);
   7905         return Air.internedToRef(elem_type.toIntern());
   7906     }
   7907 }
   7908 
   7909 fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7910     const pt = sema.pt;
   7911     const zcu = pt.zcu;
   7912     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   7913     const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type;
   7914     const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu);
   7915     assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
   7916     const elem_ty = ptr_ty.childType(zcu);
   7917     if (elem_ty.toIntern() == .anyopaque_type) {
   7918         // The pointer's actual child type is effectively unknown, so it makes
   7919         // sense to represent it with a generic poison.
   7920         return .generic_poison_type;
   7921     }
   7922     return Air.internedToRef(ptr_ty.childType(zcu).toIntern());
   7923 }
   7924 
   7925 fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7926     const pt = sema.pt;
   7927     const zcu = pt.zcu;
   7928     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   7929     const src = block.nodeOffset(un_node.src_node);
   7930     const ptr_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type;
   7931     try sema.checkMemOperand(block, src, ptr_ty);
   7932     const elem_ty = switch (ptr_ty.ptrSize(zcu)) {
   7933         .slice, .many, .c => ptr_ty.childType(zcu),
   7934         .one => ptr_ty.childType(zcu).childType(zcu),
   7935     };
   7936     return Air.internedToRef(elem_ty.toIntern());
   7937 }
   7938 
   7939 fn zirVecArrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7940     const pt = sema.pt;
   7941     const zcu = pt.zcu;
   7942     const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   7943     const vec_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type;
   7944     switch (vec_ty.zigTypeTag(zcu)) {
   7945         .array, .vector => {},
   7946         else => return sema.fail(block, block.nodeOffset(un_node.src_node), "expected array or vector type, found '{f}'", .{vec_ty.fmt(pt)}),
   7947     }
   7948     return Air.internedToRef(vec_ty.childType(zcu).toIntern());
   7949 }
   7950 
   7951 fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7952     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   7953     const len_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   7954     const elem_type_src = block.builtinCallArgSrc(inst_data.src_node, 1);
   7955     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   7956     const len: u32 = @intCast(try sema.resolveInt(block, len_src, extra.lhs, .u32, .{ .simple = .vector_length }));
   7957     const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs);
   7958     try sema.checkVectorElemType(block, elem_type_src, elem_type);
   7959     const vector_type = try sema.pt.vectorType(.{
   7960         .len = len,
   7961         .child = elem_type.toIntern(),
   7962     });
   7963     return Air.internedToRef(vector_type.toIntern());
   7964 }
   7965 
   7966 fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7967     const tracy = trace(@src());
   7968     defer tracy.end();
   7969 
   7970     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   7971     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   7972     const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node });
   7973     const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node });
   7974     const len = try sema.resolveInt(block, len_src, extra.lhs, .usize, .{ .simple = .array_length });
   7975     const elem_type = try sema.resolveType(block, elem_src, extra.rhs);
   7976     try sema.validateArrayElemType(block, elem_type, elem_src);
   7977     const array_ty = try sema.pt.arrayType(.{
   7978         .len = len,
   7979         .child = elem_type.toIntern(),
   7980     });
   7981 
   7982     return Air.internedToRef(array_ty.toIntern());
   7983 }
   7984 
   7985 fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   7986     const tracy = trace(@src());
   7987     defer tracy.end();
   7988 
   7989     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   7990     const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
   7991     const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node });
   7992     const sentinel_src = block.src(.{ .node_offset_array_type_sentinel = inst_data.src_node });
   7993     const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node });
   7994     const len = try sema.resolveInt(block, len_src, extra.len, .usize, .{ .simple = .array_length });
   7995     const elem_type = try sema.resolveType(block, elem_src, extra.elem_type);
   7996     try sema.validateArrayElemType(block, elem_type, elem_src);
   7997     const uncasted_sentinel = try sema.resolveInst(extra.sentinel);
   7998     const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src);
   7999     const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{ .simple = .array_sentinel });
   8000     const array_ty = try sema.pt.arrayType(.{
   8001         .len = len,
   8002         .sentinel = sentinel_val.toIntern(),
   8003         .child = elem_type.toIntern(),
   8004     });
   8005     try sema.checkSentinelType(block, sentinel_src, elem_type);
   8006 
   8007     return Air.internedToRef(array_ty.toIntern());
   8008 }
   8009 
   8010 fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void {
   8011     const pt = sema.pt;
   8012     const zcu = pt.zcu;
   8013     if (elem_type.zigTypeTag(zcu) == .@"opaque") {
   8014         return sema.fail(block, elem_src, "array of opaque type '{f}' not allowed", .{elem_type.fmt(pt)});
   8015     } else if (elem_type.zigTypeTag(zcu) == .noreturn) {
   8016         return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{});
   8017     }
   8018 }
   8019 
   8020 fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8021     const tracy = trace(@src());
   8022     defer tracy.end();
   8023 
   8024     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8025     if (true) {
   8026         return sema.failWithUseOfAsync(block, block.nodeOffset(inst_data.src_node));
   8027     }
   8028     const zcu = sema.zcu;
   8029     const operand_src = block.src(.{ .node_offset_anyframe_type = inst_data.src_node });
   8030     const return_type = try sema.resolveType(block, operand_src, inst_data.operand);
   8031     const anyframe_type = try zcu.anyframeType(return_type);
   8032 
   8033     return Air.internedToRef(anyframe_type.toIntern());
   8034 }
   8035 
   8036 fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8037     const tracy = trace(@src());
   8038     defer tracy.end();
   8039 
   8040     const pt = sema.pt;
   8041     const zcu = pt.zcu;
   8042     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8043     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8044     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
   8045     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
   8046     const error_set = try sema.resolveType(block, lhs_src, extra.lhs);
   8047     const payload = try sema.resolveType(block, rhs_src, extra.rhs);
   8048 
   8049     if (error_set.zigTypeTag(zcu) != .error_set) {
   8050         return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{
   8051             error_set.fmt(pt),
   8052         });
   8053     }
   8054     try sema.validateErrorUnionPayloadType(block, payload, rhs_src);
   8055     const err_union_ty = try pt.errorUnionType(error_set, payload);
   8056     return Air.internedToRef(err_union_ty.toIntern());
   8057 }
   8058 
   8059 fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void {
   8060     const pt = sema.pt;
   8061     const zcu = pt.zcu;
   8062     if (payload_ty.zigTypeTag(zcu) == .@"opaque") {
   8063         return sema.fail(block, payload_src, "error union with payload of opaque type '{f}' not allowed", .{
   8064             payload_ty.fmt(pt),
   8065         });
   8066     } else if (payload_ty.zigTypeTag(zcu) == .error_set) {
   8067         return sema.fail(block, payload_src, "error union with payload of error set type '{f}' not allowed", .{
   8068             payload_ty.fmt(pt),
   8069         });
   8070     }
   8071 }
   8072 
   8073 fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8074     _ = block;
   8075     const pt = sema.pt;
   8076     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   8077     const name = try pt.zcu.intern_pool.getOrPutString(
   8078         sema.gpa,
   8079         pt.tid,
   8080         inst_data.get(sema.code),
   8081         .no_embedded_nulls,
   8082     );
   8083     _ = try pt.getErrorValue(name);
   8084     // Create an error set type with only this error value, and return the value.
   8085     const error_set_type = try pt.singleErrorSetType(name);
   8086     return Air.internedToRef((try pt.intern(.{ .err = .{
   8087         .ty = error_set_type.toIntern(),
   8088         .name = name,
   8089     } })));
   8090 }
   8091 
   8092 fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
   8093     const tracy = trace(@src());
   8094     defer tracy.end();
   8095 
   8096     const pt = sema.pt;
   8097     const zcu = pt.zcu;
   8098     const ip = &zcu.intern_pool;
   8099     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   8100     const src = block.nodeOffset(extra.node);
   8101     const operand_src = block.builtinCallArgSrc(extra.node, 0);
   8102     const uncasted_operand = try sema.resolveInst(extra.operand);
   8103     const operand = try sema.coerce(block, .anyerror, uncasted_operand, operand_src);
   8104     const err_int_ty = try pt.errorIntType();
   8105 
   8106     if (try sema.resolveValue(operand)) |val| {
   8107         if (val.isUndef(zcu)) {
   8108             return pt.undefRef(err_int_ty);
   8109         }
   8110         const err_name = ip.indexToKey(val.toIntern()).err.name;
   8111         return Air.internedToRef((try pt.intValue(
   8112             err_int_ty,
   8113             try pt.getErrorValue(err_name),
   8114         )).toIntern());
   8115     }
   8116 
   8117     const op_ty = sema.typeOf(uncasted_operand);
   8118     switch (try sema.resolveInferredErrorSetTy(block, src, op_ty.toIntern())) {
   8119         .anyerror_type => {},
   8120         else => |err_set_ty_index| {
   8121             const names = ip.indexToKey(err_set_ty_index).error_set_type.names;
   8122             switch (names.len) {
   8123                 0 => return Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()),
   8124                 1 => return pt.intRef(err_int_ty, ip.getErrorValueIfExists(names.get(ip)[0]).?),
   8125                 else => {},
   8126             }
   8127         },
   8128     }
   8129 
   8130     try sema.requireRuntimeBlock(block, src, operand_src);
   8131     return block.addBitCast(err_int_ty, operand);
   8132 }
   8133 
   8134 fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
   8135     const tracy = trace(@src());
   8136     defer tracy.end();
   8137 
   8138     const pt = sema.pt;
   8139     const zcu = pt.zcu;
   8140     const ip = &zcu.intern_pool;
   8141     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
   8142     const src = block.nodeOffset(extra.node);
   8143     const operand_src = block.builtinCallArgSrc(extra.node, 0);
   8144     const uncasted_operand = try sema.resolveInst(extra.operand);
   8145     const err_int_ty = try pt.errorIntType();
   8146     const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src);
   8147 
   8148     if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
   8149         const int = try sema.usizeCast(block, operand_src, try value.toUnsignedIntSema(pt));
   8150         if (int > len: {
   8151             const mutate = &ip.global_error_set.mutate;
   8152             mutate.map.mutex.lock();
   8153             defer mutate.map.mutex.unlock();
   8154             break :len mutate.names.len;
   8155         } or int == 0)
   8156             return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int});
   8157         return Air.internedToRef((try pt.intern(.{ .err = .{
   8158             .ty = .anyerror_type,
   8159             .name = ip.global_error_set.shared.names.acquire().view().items(.@"0")[int - 1],
   8160         } })));
   8161     }
   8162     try sema.requireRuntimeBlock(block, src, operand_src);
   8163     if (block.wantSafety()) {
   8164         const is_lt_len = try block.addUnOp(.cmp_lt_errors_len, operand);
   8165         const zero_val = Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern());
   8166         const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val);
   8167         const ok = try block.addBinOp(.bool_and, is_lt_len, is_non_zero);
   8168         try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
   8169     }
   8170     return block.addInst(.{
   8171         .tag = .bitcast,
   8172         .data = .{ .ty_op = .{
   8173             .ty = .anyerror_type,
   8174             .operand = operand,
   8175         } },
   8176     });
   8177 }
   8178 
   8179 fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8180     const tracy = trace(@src());
   8181     defer tracy.end();
   8182 
   8183     const pt = sema.pt;
   8184     const zcu = pt.zcu;
   8185     const ip = &zcu.intern_pool;
   8186     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8187     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8188     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
   8189     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
   8190     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
   8191     const lhs = try sema.resolveInst(extra.lhs);
   8192     const rhs = try sema.resolveInst(extra.rhs);
   8193     if (sema.typeOf(lhs).zigTypeTag(zcu) == .bool and sema.typeOf(rhs).zigTypeTag(zcu) == .bool) {
   8194         const msg = msg: {
   8195             const msg = try sema.errMsg(lhs_src, "expected error set type, found 'bool'", .{});
   8196             errdefer msg.destroy(sema.gpa);
   8197             try sema.errNote(src, msg, "'||' merges error sets; 'or' performs boolean OR", .{});
   8198             break :msg msg;
   8199         };
   8200         return sema.failWithOwnedErrorMsg(block, msg);
   8201     }
   8202     const lhs_ty = try sema.analyzeAsType(block, lhs_src, lhs);
   8203     const rhs_ty = try sema.analyzeAsType(block, rhs_src, rhs);
   8204     if (lhs_ty.zigTypeTag(zcu) != .error_set)
   8205         return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{lhs_ty.fmt(pt)});
   8206     if (rhs_ty.zigTypeTag(zcu) != .error_set)
   8207         return sema.fail(block, rhs_src, "expected error set type, found '{f}'", .{rhs_ty.fmt(pt)});
   8208 
   8209     // Anything merged with anyerror is anyerror.
   8210     if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) {
   8211         return .anyerror_type;
   8212     }
   8213 
   8214     if (ip.isInferredErrorSetType(lhs_ty.toIntern())) {
   8215         switch (try sema.resolveInferredErrorSet(block, src, lhs_ty.toIntern())) {
   8216             // isAnyError might have changed from a false negative to a true
   8217             // positive after resolution.
   8218             .anyerror_type => return .anyerror_type,
   8219             else => {},
   8220         }
   8221     }
   8222     if (ip.isInferredErrorSetType(rhs_ty.toIntern())) {
   8223         switch (try sema.resolveInferredErrorSet(block, src, rhs_ty.toIntern())) {
   8224             // isAnyError might have changed from a false negative to a true
   8225             // positive after resolution.
   8226             .anyerror_type => return .anyerror_type,
   8227             else => {},
   8228         }
   8229     }
   8230 
   8231     const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty);
   8232     return Air.internedToRef(err_set_ty.toIntern());
   8233 }
   8234 
   8235 fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8236     _ = block;
   8237     const tracy = trace(@src());
   8238     defer tracy.end();
   8239 
   8240     const pt = sema.pt;
   8241     const zcu = pt.zcu;
   8242     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   8243     const name = inst_data.get(sema.code);
   8244     return Air.internedToRef((try pt.intern(.{
   8245         .enum_literal = try zcu.intern_pool.getOrPutString(sema.gpa, pt.tid, name, .no_embedded_nulls),
   8246     })));
   8247 }
   8248 
   8249 fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: bool) CompileError!Air.Inst.Ref {
   8250     const tracy = trace(@src());
   8251     defer tracy.end();
   8252 
   8253     const pt = sema.pt;
   8254     const zcu = pt.zcu;
   8255     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8256     const src = block.nodeOffset(inst_data.src_node);
   8257     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   8258     const name = try zcu.intern_pool.getOrPutString(
   8259         sema.gpa,
   8260         pt.tid,
   8261         sema.code.nullTerminatedString(extra.field_name_start),
   8262         .no_embedded_nulls,
   8263     );
   8264 
   8265     const orig_ty: Type = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse .generic_poison;
   8266 
   8267     const uncoerced_result = res: {
   8268         if (orig_ty.toIntern() == .generic_poison_type) {
   8269             // Treat this as a normal enum literal.
   8270             break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
   8271         }
   8272 
   8273         var ty = orig_ty;
   8274         while (true) switch (ty.zigTypeTag(zcu)) {
   8275             .error_union => ty = ty.errorUnionPayload(zcu),
   8276             .optional => ty = ty.optionalChild(zcu),
   8277             .pointer => ty = if (ty.isSinglePointer(zcu)) ty.childType(zcu) else break,
   8278             .enum_literal, .error_set => {
   8279                 // Treat this as a normal enum literal.
   8280                 break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
   8281             },
   8282             else => break,
   8283         };
   8284 
   8285         break :res try sema.fieldVal(block, src, Air.internedToRef(ty.toIntern()), name, src);
   8286     };
   8287 
   8288     // Decl literals cannot lookup runtime `var`s.
   8289     if (!try sema.isComptimeKnown(uncoerced_result)) {
   8290         return sema.fail(block, src, "decl literal must be comptime-known", .{});
   8291     }
   8292 
   8293     if (do_coerce) {
   8294         return sema.coerce(block, orig_ty, uncoerced_result, src);
   8295     } else {
   8296         return uncoerced_result;
   8297     }
   8298 }
   8299 
   8300 fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8301     const pt = sema.pt;
   8302     const zcu = pt.zcu;
   8303     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8304     const src = block.nodeOffset(inst_data.src_node);
   8305     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   8306     const operand = try sema.resolveInst(inst_data.operand);
   8307     const operand_ty = sema.typeOf(operand);
   8308 
   8309     const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(zcu)) {
   8310         .@"enum" => operand,
   8311         .@"union" => blk: {
   8312             try operand_ty.resolveFields(pt);
   8313             const tag_ty = operand_ty.unionTagType(zcu) orelse {
   8314                 return sema.fail(
   8315                     block,
   8316                     operand_src,
   8317                     "untagged union '{f}' cannot be converted to integer",
   8318                     .{operand_ty.fmt(pt)},
   8319                 );
   8320             };
   8321 
   8322             break :blk try sema.unionToTag(block, tag_ty, operand, operand_src);
   8323         },
   8324         else => {
   8325             return sema.fail(block, operand_src, "expected enum or tagged union, found '{f}'", .{
   8326                 operand_ty.fmt(pt),
   8327             });
   8328         },
   8329     };
   8330     const enum_tag_ty = sema.typeOf(enum_tag);
   8331     const int_tag_ty = enum_tag_ty.intTagType(zcu);
   8332 
   8333     // TODO: use correct solution
   8334     // https://github.com/ziglang/zig/issues/15909
   8335     if (enum_tag_ty.enumFieldCount(zcu) == 0 and !enum_tag_ty.isNonexhaustiveEnum(zcu)) {
   8336         return sema.fail(block, operand_src, "cannot use @intFromEnum on empty enum '{f}'", .{
   8337             enum_tag_ty.fmt(pt),
   8338         });
   8339     }
   8340 
   8341     if (try sema.typeHasOnePossibleValue(enum_tag_ty)) |opv| {
   8342         return Air.internedToRef((try pt.getCoerced(opv, int_tag_ty)).toIntern());
   8343     }
   8344 
   8345     if (try sema.resolveValue(enum_tag)) |enum_tag_val| {
   8346         if (enum_tag_val.isUndef(zcu)) {
   8347             return pt.undefRef(int_tag_ty);
   8348         }
   8349 
   8350         const val = try enum_tag_val.intFromEnum(enum_tag_ty, pt);
   8351         return Air.internedToRef(val.toIntern());
   8352     }
   8353 
   8354     try sema.requireRuntimeBlock(block, src, operand_src);
   8355     return block.addBitCast(int_tag_ty, enum_tag);
   8356 }
   8357 
   8358 fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8359     const pt = sema.pt;
   8360     const zcu = pt.zcu;
   8361     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8362     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   8363     const src = block.nodeOffset(inst_data.src_node);
   8364     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   8365     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt");
   8366     const operand = try sema.resolveInst(extra.rhs);
   8367     const operand_ty = sema.typeOf(operand);
   8368 
   8369     if (dest_ty.zigTypeTag(zcu) != .@"enum") {
   8370         return sema.fail(block, src, "expected enum, found '{f}'", .{dest_ty.fmt(pt)});
   8371     }
   8372     _ = try sema.checkIntType(block, operand_src, operand_ty);
   8373 
   8374     if (try sema.resolveValue(operand)) |int_val| {
   8375         if (dest_ty.isNonexhaustiveEnum(zcu)) {
   8376             const int_tag_ty = dest_ty.intTagType(zcu);
   8377             if (try sema.intFitsInType(int_val, int_tag_ty, null)) {
   8378                 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
   8379             }
   8380             return sema.fail(block, src, "int value '{f}' out of range of non-exhaustive enum '{f}'", .{
   8381                 int_val.fmtValueSema(pt, sema), dest_ty.fmt(pt),
   8382             });
   8383         }
   8384         if (int_val.isUndef(zcu)) {
   8385             return sema.failWithUseOfUndef(block, operand_src);
   8386         }
   8387         if (!(try sema.enumHasInt(dest_ty, int_val))) {
   8388             return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{
   8389                 dest_ty.fmt(pt), int_val.fmtValueSema(pt, sema),
   8390             });
   8391         }
   8392         return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
   8393     }
   8394 
   8395     if (dest_ty.intTagType(zcu).zigTypeTag(zcu) == .comptime_int) {
   8396         return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_enum });
   8397     }
   8398 
   8399     if (try sema.typeHasOnePossibleValue(dest_ty)) |opv| {
   8400         if (block.wantSafety()) {
   8401             // The operand is runtime-known but the result is comptime-known. In
   8402             // this case we still need a safety check.
   8403             const expect_int_val = switch (zcu.intern_pool.indexToKey(opv.toIntern())) {
   8404                 .enum_tag => |enum_tag| enum_tag.int,
   8405                 else => unreachable,
   8406             };
   8407             const expect_int_coerced = try pt.getCoerced(.fromInterned(expect_int_val), operand_ty);
   8408             const ok = try block.addBinOp(.cmp_eq, operand, Air.internedToRef(expect_int_coerced.toIntern()));
   8409             try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
   8410         }
   8411         return Air.internedToRef(opv.toIntern());
   8412     }
   8413 
   8414     try sema.requireRuntimeBlock(block, src, operand_src);
   8415     if (block.wantSafety()) {
   8416         try sema.preparePanicId(src, .invalid_enum_value);
   8417         return block.addTyOp(.intcast_safe, dest_ty, operand);
   8418     }
   8419     return block.addTyOp(.intcast, dest_ty, operand);
   8420 }
   8421 
   8422 /// Pointer in, pointer out.
   8423 fn zirOptionalPayloadPtr(
   8424     sema: *Sema,
   8425     block: *Block,
   8426     inst: Zir.Inst.Index,
   8427     safety_check: bool,
   8428 ) CompileError!Air.Inst.Ref {
   8429     const tracy = trace(@src());
   8430     defer tracy.end();
   8431 
   8432     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8433     const optional_ptr = try sema.resolveInst(inst_data.operand);
   8434     const src = block.nodeOffset(inst_data.src_node);
   8435 
   8436     return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false);
   8437 }
   8438 
   8439 fn analyzeOptionalPayloadPtr(
   8440     sema: *Sema,
   8441     block: *Block,
   8442     src: LazySrcLoc,
   8443     optional_ptr: Air.Inst.Ref,
   8444     safety_check: bool,
   8445     initializing: bool,
   8446 ) CompileError!Air.Inst.Ref {
   8447     const pt = sema.pt;
   8448     const zcu = pt.zcu;
   8449     const optional_ptr_ty = sema.typeOf(optional_ptr);
   8450     assert(optional_ptr_ty.zigTypeTag(zcu) == .pointer);
   8451 
   8452     const opt_type = optional_ptr_ty.childType(zcu);
   8453     if (opt_type.zigTypeTag(zcu) != .optional) {
   8454         return sema.failWithExpectedOptionalType(block, src, opt_type);
   8455     }
   8456 
   8457     const child_type = opt_type.optionalChild(zcu);
   8458     const child_pointer = try pt.ptrTypeSema(.{
   8459         .child = child_type.toIntern(),
   8460         .flags = .{
   8461             .is_const = optional_ptr_ty.isConstPtr(zcu),
   8462             .address_space = optional_ptr_ty.ptrAddressSpace(zcu),
   8463         },
   8464     });
   8465 
   8466     if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| {
   8467         if (initializing) {
   8468             if (sema.isComptimeMutablePtr(ptr_val)) {
   8469                 // Set the optional to non-null at comptime.
   8470                 // If the payload is OPV, we must use that value instead of undef.
   8471                 const payload_val = try sema.typeHasOnePossibleValue(child_type) orelse try pt.undefValue(child_type);
   8472                 const opt_val = try pt.intern(.{ .opt = .{
   8473                     .ty = opt_type.toIntern(),
   8474                     .val = payload_val.toIntern(),
   8475                 } });
   8476                 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(opt_val), opt_type);
   8477             } else {
   8478                 // Emit runtime instructions to set the optional non-null bit.
   8479                 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
   8480                 try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr);
   8481             }
   8482             return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern());
   8483         }
   8484         if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {
   8485             if (val.isNull(zcu)) {
   8486                 return sema.fail(block, src, "unable to unwrap null", .{});
   8487             }
   8488             return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern());
   8489         }
   8490     }
   8491 
   8492     try sema.requireRuntimeBlock(block, src, null);
   8493     if (safety_check and block.wantSafety()) {
   8494         const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
   8495         try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
   8496     }
   8497 
   8498     if (initializing) {
   8499         const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
   8500         try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr);
   8501         return opt_payload_ptr;
   8502     } else {
   8503         return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr);
   8504     }
   8505 }
   8506 
   8507 /// Value in, value out.
   8508 fn zirOptionalPayload(
   8509     sema: *Sema,
   8510     block: *Block,
   8511     inst: Zir.Inst.Index,
   8512     safety_check: bool,
   8513 ) CompileError!Air.Inst.Ref {
   8514     const tracy = trace(@src());
   8515     defer tracy.end();
   8516 
   8517     const pt = sema.pt;
   8518     const zcu = pt.zcu;
   8519     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8520     const src = block.nodeOffset(inst_data.src_node);
   8521     const operand = try sema.resolveInst(inst_data.operand);
   8522     const operand_ty = sema.typeOf(operand);
   8523     const result_ty = switch (operand_ty.zigTypeTag(zcu)) {
   8524         .optional => operand_ty.optionalChild(zcu),
   8525         .pointer => t: {
   8526             if (operand_ty.ptrSize(zcu) != .c) {
   8527                 return sema.failWithExpectedOptionalType(block, src, operand_ty);
   8528             }
   8529             // TODO https://github.com/ziglang/zig/issues/6597
   8530             if (true) break :t operand_ty;
   8531             const ptr_info = operand_ty.ptrInfo(zcu);
   8532             break :t try pt.ptrTypeSema(.{
   8533                 .child = ptr_info.child,
   8534                 .flags = .{
   8535                     .alignment = ptr_info.flags.alignment,
   8536                     .is_const = ptr_info.flags.is_const,
   8537                     .is_volatile = ptr_info.flags.is_volatile,
   8538                     .is_allowzero = ptr_info.flags.is_allowzero,
   8539                     .address_space = ptr_info.flags.address_space,
   8540                 },
   8541             });
   8542         },
   8543         else => return sema.failWithExpectedOptionalType(block, src, operand_ty),
   8544     };
   8545 
   8546     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
   8547         if (val.optionalValue(zcu)) |payload| return Air.internedToRef(payload.toIntern());
   8548         if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{});
   8549         if (safety_check and block.wantSafety()) {
   8550             try sema.safetyPanic(block, src, .unwrap_null);
   8551         } else {
   8552             _ = try block.addNoOp(.unreach);
   8553         }
   8554         return .unreachable_value;
   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, operand);
   8560         try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
   8561     }
   8562     return block.addTyOp(.optional_payload, result_ty, operand);
   8563 }
   8564 
   8565 /// Value in, value out
   8566 fn zirErrUnionPayload(
   8567     sema: *Sema,
   8568     block: *Block,
   8569     inst: Zir.Inst.Index,
   8570 ) CompileError!Air.Inst.Ref {
   8571     const tracy = trace(@src());
   8572     defer tracy.end();
   8573 
   8574     const pt = sema.pt;
   8575     const zcu = pt.zcu;
   8576     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8577     const src = block.nodeOffset(inst_data.src_node);
   8578     const operand = try sema.resolveInst(inst_data.operand);
   8579     const operand_src = src;
   8580     const err_union_ty = sema.typeOf(operand);
   8581     if (err_union_ty.zigTypeTag(zcu) != .error_union) {
   8582         return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{
   8583             err_union_ty.fmt(pt),
   8584         });
   8585     }
   8586     return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false);
   8587 }
   8588 
   8589 fn analyzeErrUnionPayload(
   8590     sema: *Sema,
   8591     block: *Block,
   8592     src: LazySrcLoc,
   8593     err_union_ty: Type,
   8594     operand: Air.Inst.Ref,
   8595     operand_src: LazySrcLoc,
   8596     safety_check: bool,
   8597 ) CompileError!Air.Inst.Ref {
   8598     const pt = sema.pt;
   8599     const zcu = pt.zcu;
   8600     const payload_ty = err_union_ty.errorUnionPayload(zcu);
   8601     if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
   8602         if (val.getErrorName(zcu).unwrap()) |name| {
   8603             return sema.failWithComptimeErrorRetTrace(block, src, name);
   8604         }
   8605         return Air.internedToRef(zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.payload);
   8606     }
   8607 
   8608     try sema.requireRuntimeBlock(block, src, null);
   8609 
   8610     // If the error set has no fields then no safety check is needed.
   8611     if (safety_check and block.wantSafety() and
   8612         !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu))
   8613     {
   8614         try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
   8615     }
   8616 
   8617     if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_only_value| {
   8618         return Air.internedToRef(payload_only_value.toIntern());
   8619     }
   8620 
   8621     return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
   8622 }
   8623 
   8624 /// Pointer in, pointer out.
   8625 fn zirErrUnionPayloadPtr(
   8626     sema: *Sema,
   8627     block: *Block,
   8628     inst: Zir.Inst.Index,
   8629 ) CompileError!Air.Inst.Ref {
   8630     const tracy = trace(@src());
   8631     defer tracy.end();
   8632 
   8633     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8634     const operand = try sema.resolveInst(inst_data.operand);
   8635     const src = block.nodeOffset(inst_data.src_node);
   8636 
   8637     return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
   8638 }
   8639 
   8640 fn analyzeErrUnionPayloadPtr(
   8641     sema: *Sema,
   8642     block: *Block,
   8643     src: LazySrcLoc,
   8644     operand: Air.Inst.Ref,
   8645     safety_check: bool,
   8646     initializing: bool,
   8647 ) CompileError!Air.Inst.Ref {
   8648     const pt = sema.pt;
   8649     const zcu = pt.zcu;
   8650     const operand_ty = sema.typeOf(operand);
   8651     assert(operand_ty.zigTypeTag(zcu) == .pointer);
   8652 
   8653     if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) {
   8654         return sema.fail(block, src, "expected error union type, found '{f}'", .{
   8655             operand_ty.childType(zcu).fmt(pt),
   8656         });
   8657     }
   8658 
   8659     const err_union_ty = operand_ty.childType(zcu);
   8660     const payload_ty = err_union_ty.errorUnionPayload(zcu);
   8661     const operand_pointer_ty = try pt.ptrTypeSema(.{
   8662         .child = payload_ty.toIntern(),
   8663         .flags = .{
   8664             .is_const = operand_ty.isConstPtr(zcu),
   8665             .address_space = operand_ty.ptrAddressSpace(zcu),
   8666         },
   8667     });
   8668 
   8669     if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| {
   8670         if (initializing) {
   8671             if (sema.isComptimeMutablePtr(ptr_val)) {
   8672                 // Set the error union to non-error at comptime.
   8673                 // If the payload is OPV, we must use that value instead of undef.
   8674                 const payload_val = try sema.typeHasOnePossibleValue(payload_ty) orelse try pt.undefValue(payload_ty);
   8675                 const eu_val = try pt.intern(.{ .error_union = .{
   8676                     .ty = err_union_ty.toIntern(),
   8677                     .val = .{ .payload = payload_val.toIntern() },
   8678                 } });
   8679                 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(eu_val), err_union_ty);
   8680             } else {
   8681                 // Emit runtime instructions to set the error union error code.
   8682                 try sema.requireRuntimeBlock(block, src, null);
   8683                 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
   8684                 try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr);
   8685             }
   8686             return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern());
   8687         }
   8688         if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {
   8689             if (val.getErrorName(zcu).unwrap()) |name| {
   8690                 return sema.failWithComptimeErrorRetTrace(block, src, name);
   8691             }
   8692             return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern());
   8693         }
   8694     }
   8695 
   8696     try sema.requireRuntimeBlock(block, src, null);
   8697 
   8698     // If the error set has no fields then no safety check is needed.
   8699     if (safety_check and block.wantSafety() and
   8700         !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu))
   8701     {
   8702         try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
   8703     }
   8704 
   8705     if (initializing) {
   8706         const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
   8707         try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr);
   8708         return eu_payload_ptr;
   8709     } else {
   8710         return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand);
   8711     }
   8712 }
   8713 
   8714 /// Value in, value out
   8715 fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8716     const tracy = trace(@src());
   8717     defer tracy.end();
   8718 
   8719     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8720     const src = block.nodeOffset(inst_data.src_node);
   8721     const operand = try sema.resolveInst(inst_data.operand);
   8722     return sema.analyzeErrUnionCode(block, src, operand);
   8723 }
   8724 
   8725 /// If `operand` is comptime-known, asserts that it is an error value rather than a payload value.
   8726 fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   8727     const pt = sema.pt;
   8728     const zcu = pt.zcu;
   8729     const operand_ty = sema.typeOf(operand);
   8730     if (operand_ty.zigTypeTag(zcu) != .error_union) {
   8731         return sema.fail(block, src, "expected error union type, found '{f}'", .{
   8732             operand_ty.fmt(pt),
   8733         });
   8734     }
   8735 
   8736     const result_ty = operand_ty.errorUnionSet(zcu);
   8737 
   8738     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
   8739         return Air.internedToRef((try pt.intern(.{ .err = .{
   8740             .ty = result_ty.toIntern(),
   8741             .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
   8742         } })));
   8743     }
   8744 
   8745     try sema.requireRuntimeBlock(block, src, null);
   8746     return block.addTyOp(.unwrap_errunion_err, result_ty, operand);
   8747 }
   8748 
   8749 /// Pointer in, value out
   8750 fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   8751     const tracy = trace(@src());
   8752     defer tracy.end();
   8753 
   8754     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   8755     const src = block.nodeOffset(inst_data.src_node);
   8756     const operand = try sema.resolveInst(inst_data.operand);
   8757     return sema.analyzeErrUnionCodePtr(block, src, operand);
   8758 }
   8759 
   8760 fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
   8761     const pt = sema.pt;
   8762     const zcu = pt.zcu;
   8763     const operand_ty = sema.typeOf(operand);
   8764     assert(operand_ty.zigTypeTag(zcu) == .pointer);
   8765 
   8766     if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) {
   8767         return sema.fail(block, src, "expected error union type, found '{f}'", .{
   8768             operand_ty.childType(zcu).fmt(pt),
   8769         });
   8770     }
   8771 
   8772     const result_ty = operand_ty.childType(zcu).errorUnionSet(zcu);
   8773 
   8774     if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
   8775         if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
   8776             assert(val.getErrorName(zcu) != .none);
   8777             return Air.internedToRef((try pt.intern(.{ .err = .{
   8778                 .ty = result_ty.toIntern(),
   8779                 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
   8780             } })));
   8781         }
   8782     }
   8783 
   8784     try sema.requireRuntimeBlock(block, src, null);
   8785     return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand);
   8786 }
   8787 
   8788 fn zirFunc(
   8789     sema: *Sema,
   8790     block: *Block,
   8791     inst: Zir.Inst.Index,
   8792     inferred_error_set: bool,
   8793 ) CompileError!Air.Inst.Ref {
   8794     const pt = sema.pt;
   8795     const zcu = pt.zcu;
   8796     const ip = &zcu.intern_pool;
   8797     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   8798     const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
   8799     const target = zcu.getTarget();
   8800     const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node });
   8801     const src = block.nodeOffset(inst_data.src_node);
   8802 
   8803     var extra_index = extra.end;
   8804 
   8805     const ret_ty: Type = if (extra.data.ret_ty.is_generic)
   8806         .generic_poison
   8807     else switch (extra.data.ret_ty.body_len) {
   8808         0 => .void,
   8809         1 => blk: {
   8810             const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
   8811             extra_index += 1;
   8812             break :blk try sema.resolveType(block, ret_ty_src, ret_ty_ref);
   8813         },
   8814         else => blk: {
   8815             const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_ty.body_len);
   8816             extra_index += ret_ty_body.len;
   8817 
   8818             const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, .type, .{ .simple = .function_ret_ty });
   8819             break :blk ret_ty_val.toType();
   8820         },
   8821     };
   8822 
   8823     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
   8824     const has_body = extra.data.body_len != 0;
   8825     if (has_body) {
   8826         extra_index += extra.data.body_len;
   8827         src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
   8828     }
   8829 
   8830     // If this instruction has a body, then it's a function declaration, and we decide
   8831     // the callconv based on whether it is exported. Otherwise, the callconv defaults
   8832     // to `.auto`.
   8833     const cc: std.builtin.CallingConvention = if (has_body) cc: {
   8834         const func_decl_nav = sema.owner.unwrap().nav_val;
   8835         const fn_is_exported = exported: {
   8836             const decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(ip) orelse return error.AnalysisFail;
   8837             const zir_decl = sema.code.getDeclaration(decl_inst);
   8838             break :exported zir_decl.linkage == .@"export";
   8839         };
   8840         if (fn_is_exported) {
   8841             break :cc target.cCallingConvention() orelse {
   8842                 // This target has no default C calling convention. We sometimes trigger a similar
   8843                 // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency,
   8844                 // let's eval that now and just get the transitive error. (It's guaranteed to error
   8845                 // because it does the exact `cCallingConvention` call we just did.)
   8846                 const cc_type = try sema.getBuiltinType(src, .CallingConvention);
   8847                 _ = try sema.namespaceLookupVal(
   8848                     block,
   8849                     LazySrcLoc.unneeded,
   8850                     cc_type.getNamespaceIndex(zcu),
   8851                     try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls),
   8852                 );
   8853                 // The above should have errored.
   8854                 @panic("std.builtin is corrupt");
   8855             };
   8856         } else {
   8857             break :cc .auto;
   8858         }
   8859     } else .auto;
   8860 
   8861     return sema.funcCommon(
   8862         block,
   8863         inst_data.src_node,
   8864         inst,
   8865         cc,
   8866         ret_ty,
   8867         false,
   8868         inferred_error_set,
   8869         has_body,
   8870         src_locs,
   8871         0,
   8872         false,
   8873     );
   8874 }
   8875 
   8876 fn resolveGenericBody(
   8877     sema: *Sema,
   8878     block: *Block,
   8879     src: LazySrcLoc,
   8880     body: []const Zir.Inst.Index,
   8881     func_inst: Zir.Inst.Index,
   8882     dest_ty: Type,
   8883     reason: ComptimeReason,
   8884 ) !Value {
   8885     assert(body.len != 0);
   8886 
   8887     // Make sure any nested param instructions don't clobber our work.
   8888     const prev_params = block.params;
   8889     block.params = .{};
   8890     defer {
   8891         block.params = prev_params;
   8892     }
   8893 
   8894     const uncasted = try sema.resolveInlineBody(block, body, func_inst);
   8895     const result = try sema.coerce(block, dest_ty, uncasted, src);
   8896     return sema.resolveConstDefinedValue(block, src, result, reason);
   8897 }
   8898 
   8899 pub fn handleExternLibName(
   8900     sema: *Sema,
   8901     block: *Block,
   8902     src_loc: LazySrcLoc,
   8903     lib_name: []const u8,
   8904 ) CompileError!void {
   8905     blk: {
   8906         const pt = sema.pt;
   8907         const zcu = pt.zcu;
   8908         const comp = zcu.comp;
   8909         const target = zcu.getTarget();
   8910         log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
   8911         if (std.zig.target.isLibCLibName(target, lib_name)) {
   8912             if (!comp.config.link_libc) {
   8913                 return sema.fail(
   8914                     block,
   8915                     src_loc,
   8916                     "dependency on libc must be explicitly specified in the build command",
   8917                     .{},
   8918                 );
   8919             }
   8920             break :blk;
   8921         }
   8922         if (std.zig.target.isLibCxxLibName(target, lib_name)) {
   8923             if (!comp.config.link_libcpp) return sema.fail(
   8924                 block,
   8925                 src_loc,
   8926                 "dependency on libc++ must be explicitly specified in the build command",
   8927                 .{},
   8928             );
   8929             break :blk;
   8930         }
   8931         if (mem.eql(u8, lib_name, "unwind")) {
   8932             if (!comp.config.link_libunwind) return sema.fail(
   8933                 block,
   8934                 src_loc,
   8935                 "dependency on libunwind must be explicitly specified in the build command",
   8936                 .{},
   8937             );
   8938             break :blk;
   8939         }
   8940         if (!target.cpu.arch.isWasm() and !block.ownerModule().pic) {
   8941             return sema.fail(
   8942                 block,
   8943                 src_loc,
   8944                 "dependency on dynamic library '{s}' requires enabling Position Independent Code; fixed by '-l{s}' or '-fPIC'",
   8945                 .{ lib_name, lib_name },
   8946             );
   8947         }
   8948     }
   8949 }
   8950 
   8951 /// These are calling conventions that are confirmed to work with variadic functions.
   8952 /// Any calling conventions not included here are either not yet verified to work with variadic
   8953 /// functions or there are no more other calling conventions that support variadic functions.
   8954 const calling_conventions_supporting_var_args = [_]std.builtin.CallingConvention.Tag{
   8955     .x86_64_sysv,
   8956     .x86_64_win,
   8957     .x86_sysv,
   8958     .x86_win,
   8959     .aarch64_aapcs,
   8960     .aarch64_aapcs_darwin,
   8961     .aarch64_aapcs_win,
   8962     .aarch64_vfabi,
   8963     .aarch64_vfabi_sve,
   8964     .arm_aapcs,
   8965     .arm_aapcs_vfp,
   8966     .mips64_n64,
   8967     .mips64_n32,
   8968     .mips_o32,
   8969     .riscv64_lp64,
   8970     .riscv64_lp64_v,
   8971     .riscv32_ilp32,
   8972     .riscv32_ilp32_v,
   8973     .sparc64_sysv,
   8974     .sparc_sysv,
   8975     .powerpc64_elf,
   8976     .powerpc64_elf_altivec,
   8977     .powerpc64_elf_v2,
   8978     .powerpc_sysv,
   8979     .powerpc_sysv_altivec,
   8980     .powerpc_aix,
   8981     .powerpc_aix_altivec,
   8982     .wasm_mvp,
   8983     .arc_sysv,
   8984     .avr_gnu,
   8985     .bpf_std,
   8986     .csky_sysv,
   8987     .hexagon_sysv,
   8988     .hexagon_sysv_hvx,
   8989     .lanai_sysv,
   8990     .loongarch64_lp64,
   8991     .loongarch32_ilp32,
   8992     .m68k_sysv,
   8993     .m68k_gnu,
   8994     .m68k_rtd,
   8995     .msp430_eabi,
   8996     .s390x_sysv,
   8997     .s390x_sysv_vx,
   8998     .ve_sysv,
   8999     .xcore_xs1,
   9000     .xcore_xs2,
   9001     .xtensa_call0,
   9002     .xtensa_windowed,
   9003 };
   9004 fn callConvSupportsVarArgs(cc: std.builtin.CallingConvention.Tag) bool {
   9005     return for (calling_conventions_supporting_var_args) |supported_cc| {
   9006         if (cc == supported_cc) return true;
   9007     } else false;
   9008 }
   9009 fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.builtin.CallingConvention.Tag) CompileError!void {
   9010     const CallingConventionsSupportingVarArgsList = struct {
   9011         arch: std.Target.Cpu.Arch,
   9012         pub fn format(ctx: @This(), w: *std.io.Writer) std.io.Writer.Error!void {
   9013             var first = true;
   9014             for (calling_conventions_supporting_var_args) |cc_inner| {
   9015                 for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| {
   9016                     if (supported_arch == ctx.arch) break;
   9017                 } else continue; // callconv not supported by this arch
   9018                 if (!first) {
   9019                     try w.writeAll(", ");
   9020                 }
   9021                 first = false;
   9022                 try w.print("'{s}'", .{@tagName(cc_inner)});
   9023             }
   9024         }
   9025     };
   9026 
   9027     if (!callConvSupportsVarArgs(cc)) {
   9028         return sema.failWithOwnedErrorMsg(block, msg: {
   9029             const msg = try sema.errMsg(src, "variadic function does not support '{s}' calling convention", .{@tagName(cc)});
   9030             errdefer msg.destroy(sema.gpa);
   9031             const target = sema.pt.zcu.getTarget();
   9032             try sema.errNote(src, msg, "supported calling conventions: {f}", .{CallingConventionsSupportingVarArgsList{ .arch = target.cpu.arch }});
   9033             break :msg msg;
   9034         });
   9035     }
   9036 }
   9037 
   9038 fn callConvIsCallable(cc: std.builtin.CallingConvention.Tag) bool {
   9039     return switch (cc) {
   9040         .naked,
   9041 
   9042         .arm_interrupt,
   9043         .avr_interrupt,
   9044         .avr_signal,
   9045         .csky_interrupt,
   9046         .m68k_interrupt,
   9047         .mips_interrupt,
   9048         .mips64_interrupt,
   9049         .riscv32_interrupt,
   9050         .riscv64_interrupt,
   9051         .x86_interrupt,
   9052         .x86_64_interrupt,
   9053 
   9054         .amdgcn_kernel,
   9055         .nvptx_kernel,
   9056         .spirv_kernel,
   9057         .spirv_fragment,
   9058         .spirv_vertex,
   9059         => false,
   9060 
   9061         else => true,
   9062     };
   9063 }
   9064 
   9065 fn checkMergeAllowed(sema: *Sema, block: *Block, src: LazySrcLoc, peer_ty: Type) !void {
   9066     const pt = sema.pt;
   9067     const zcu = pt.zcu;
   9068     const target = zcu.getTarget();
   9069 
   9070     if (!peer_ty.isPtrAtRuntime(zcu)) {
   9071         return;
   9072     }
   9073 
   9074     const as = peer_ty.ptrAddressSpace(zcu);
   9075     if (!target_util.arePointersLogical(target, as)) {
   9076         return;
   9077     }
   9078 
   9079     return sema.failWithOwnedErrorMsg(block, msg: {
   9080         const msg = try sema.errMsg(src, "value with non-mergable pointer type '{f}' depends on runtime control flow", .{peer_ty.fmt(pt)});
   9081         errdefer msg.destroy(sema.gpa);
   9082 
   9083         const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
   9084         try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
   9085 
   9086         const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);
   9087         try sema.errNote(src, msg, "pointers with address space '{s}' cannot be returned from a branch on target {s}-{s} by compiler backend {s}", .{
   9088             @tagName(as),
   9089             @tagName(target.cpu.arch.family()),
   9090             @tagName(target.os.tag),
   9091             @tagName(backend),
   9092         });
   9093 
   9094         break :msg msg;
   9095     });
   9096 }
   9097 
   9098 const Section = union(enum) {
   9099     generic,
   9100     default,
   9101     explicit: InternPool.NullTerminatedString,
   9102 };
   9103 
   9104 fn funcCommon(
   9105     sema: *Sema,
   9106     block: *Block,
   9107     src_node_offset: std.zig.Ast.Node.Offset,
   9108     func_inst: Zir.Inst.Index,
   9109     cc: std.builtin.CallingConvention,
   9110     /// this might be Type.generic_poison
   9111     bare_return_type: Type,
   9112     var_args: bool,
   9113     inferred_error_set: bool,
   9114     has_body: bool,
   9115     src_locs: Zir.Inst.Func.SrcLocs,
   9116     noalias_bits: u32,
   9117     is_noinline: bool,
   9118 ) CompileError!Air.Inst.Ref {
   9119     const pt = sema.pt;
   9120     const zcu = pt.zcu;
   9121     const gpa = sema.gpa;
   9122     const target = zcu.getTarget();
   9123     const ip = &zcu.intern_pool;
   9124     const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset });
   9125     const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset });
   9126     const func_src = block.nodeOffset(src_node_offset);
   9127 
   9128     const ret_ty_requires_comptime = try bare_return_type.comptimeOnlySema(pt);
   9129     var is_generic = bare_return_type.isGenericPoison() or ret_ty_requires_comptime;
   9130 
   9131     var comptime_bits: u32 = 0;
   9132     for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| {
   9133         const param_ty: Type = .fromInterned(param_ty_ip);
   9134         const is_noalias = blk: {
   9135             const index = std.math.cast(u5, i) orelse break :blk false;
   9136             break :blk @as(u1, @truncate(noalias_bits >> index)) != 0;
   9137         };
   9138         const param_src = block.src(.{ .fn_proto_param = .{
   9139             .fn_proto_node_offset = src_node_offset,
   9140             .param_index = @intCast(i),
   9141         } });
   9142         const param_ty_comptime = try param_ty.comptimeOnlySema(pt);
   9143         const param_ty_generic = param_ty.isGenericPoison();
   9144         if (param_is_comptime or param_ty_comptime or param_ty_generic) {
   9145             is_generic = true;
   9146         }
   9147         if (param_is_comptime) {
   9148             comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error
   9149         }
   9150         if (param_is_comptime and !target_util.fnCallConvAllowsZigTypes(cc)) {
   9151             return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
   9152         }
   9153         if (param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc)) {
   9154             return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{s}'", .{@tagName(cc)});
   9155         }
   9156         if (!param_ty.isValidParamType(zcu)) {
   9157             const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
   9158             return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{
   9159                 opaque_str, param_ty.fmt(pt),
   9160             });
   9161         }
   9162         if (!param_ty_generic and !target_util.fnCallConvAllowsZigTypes(cc) and !try sema.validateExternType(param_ty, .param_ty)) {
   9163             const msg = msg: {
   9164                 const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{s}'", .{
   9165                     param_ty.fmt(pt), @tagName(cc),
   9166                 });
   9167                 errdefer msg.destroy(sema.gpa);
   9168 
   9169                 try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty);
   9170 
   9171                 try sema.addDeclaredHereNote(msg, param_ty);
   9172                 break :msg msg;
   9173             };
   9174             return sema.failWithOwnedErrorMsg(block, msg);
   9175         }
   9176         if (param_ty_comptime and !param_is_comptime and has_body and !block.isComptime()) {
   9177             const msg = msg: {
   9178                 const msg = try sema.errMsg(param_src, "parameter of type '{f}' must be declared comptime", .{
   9179                     param_ty.fmt(pt),
   9180                 });
   9181                 errdefer msg.destroy(sema.gpa);
   9182 
   9183                 try sema.explainWhyTypeIsComptime(msg, param_src, param_ty);
   9184 
   9185                 try sema.addDeclaredHereNote(msg, param_ty);
   9186                 break :msg msg;
   9187             };
   9188             return sema.failWithOwnedErrorMsg(block, msg);
   9189         }
   9190         if (!param_ty_generic and is_noalias and
   9191             !(param_ty.zigTypeTag(zcu) == .pointer or param_ty.isPtrLikeOptional(zcu)))
   9192         {
   9193             return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{});
   9194         }
   9195         switch (cc) {
   9196             .x86_64_interrupt, .x86_interrupt => {
   9197                 const err_code_size = target.ptrBitWidth();
   9198                 switch (i) {
   9199                     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)}),
   9200                     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 }),
   9201                     else => return sema.fail(block, param_src, "'{s}' calling convention supports up to 2 parameters, found {d}", .{ @tagName(cc), i + 1 }),
   9202                 }
   9203             },
   9204             .arm_interrupt,
   9205             .mips64_interrupt,
   9206             .mips_interrupt,
   9207             .riscv64_interrupt,
   9208             .riscv32_interrupt,
   9209             .avr_interrupt,
   9210             .csky_interrupt,
   9211             .m68k_interrupt,
   9212             .avr_signal,
   9213             => return sema.fail(block, param_src, "parameters are not allowed with '{s}' calling convention", .{@tagName(cc)}),
   9214             else => {},
   9215         }
   9216     }
   9217 
   9218     if (var_args) {
   9219         if (is_generic) {
   9220             return sema.fail(block, func_src, "generic function cannot be variadic", .{});
   9221         }
   9222         const va_args_src = block.src(.{
   9223             .fn_proto_param = .{
   9224                 .fn_proto_node_offset = src_node_offset,
   9225                 .param_index = @intCast(block.params.len), // va_arg must be the last parameter
   9226             },
   9227         });
   9228         try sema.checkCallConvSupportsVarArgs(block, va_args_src, cc);
   9229     }
   9230 
   9231     const ret_poison = bare_return_type.isGenericPoison();
   9232 
   9233     const param_types = block.params.items(.ty);
   9234 
   9235     if (inferred_error_set) {
   9236         assert(has_body);
   9237         if (!ret_poison)
   9238             try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src);
   9239         const func_index = try ip.getFuncDeclIes(gpa, pt.tid, .{
   9240             .owner_nav = sema.owner.unwrap().nav_val,
   9241 
   9242             .param_types = param_types,
   9243             .noalias_bits = noalias_bits,
   9244             .comptime_bits = comptime_bits,
   9245             .bare_return_type = bare_return_type.toIntern(),
   9246             .cc = cc,
   9247             .is_var_args = var_args,
   9248             .is_generic = is_generic,
   9249             .is_noinline = is_noinline,
   9250 
   9251             .zir_body_inst = try block.trackZir(func_inst),
   9252             .lbrace_line = src_locs.lbrace_line,
   9253             .rbrace_line = src_locs.rbrace_line,
   9254             .lbrace_column = @as(u16, @truncate(src_locs.columns)),
   9255             .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
   9256         });
   9257         return finishFunc(
   9258             sema,
   9259             block,
   9260             func_index,
   9261             .none,
   9262             ret_poison,
   9263             bare_return_type,
   9264             ret_ty_src,
   9265             cc,
   9266             ret_ty_requires_comptime,
   9267             func_inst,
   9268             cc_src,
   9269             is_noinline,
   9270         );
   9271     }
   9272 
   9273     const func_ty = try ip.getFuncType(gpa, pt.tid, .{
   9274         .param_types = param_types,
   9275         .noalias_bits = noalias_bits,
   9276         .comptime_bits = comptime_bits,
   9277         .return_type = bare_return_type.toIntern(),
   9278         .cc = cc,
   9279         .is_var_args = var_args,
   9280         .is_generic = is_generic,
   9281         .is_noinline = is_noinline,
   9282     });
   9283 
   9284     if (has_body) {
   9285         const func_index = try ip.getFuncDecl(gpa, pt.tid, .{
   9286             .owner_nav = sema.owner.unwrap().nav_val,
   9287             .ty = func_ty,
   9288             .cc = cc,
   9289             .is_noinline = is_noinline,
   9290             .zir_body_inst = try block.trackZir(func_inst),
   9291             .lbrace_line = src_locs.lbrace_line,
   9292             .rbrace_line = src_locs.rbrace_line,
   9293             .lbrace_column = @as(u16, @truncate(src_locs.columns)),
   9294             .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
   9295         });
   9296         return finishFunc(
   9297             sema,
   9298             block,
   9299             func_index,
   9300             func_ty,
   9301             ret_poison,
   9302             bare_return_type,
   9303             ret_ty_src,
   9304             cc,
   9305             ret_ty_requires_comptime,
   9306             func_inst,
   9307             cc_src,
   9308             is_noinline,
   9309         );
   9310     }
   9311 
   9312     return finishFunc(
   9313         sema,
   9314         block,
   9315         .none,
   9316         func_ty,
   9317         ret_poison,
   9318         bare_return_type,
   9319         ret_ty_src,
   9320         cc,
   9321         ret_ty_requires_comptime,
   9322         func_inst,
   9323         cc_src,
   9324         is_noinline,
   9325     );
   9326 }
   9327 
   9328 fn finishFunc(
   9329     sema: *Sema,
   9330     block: *Block,
   9331     opt_func_index: InternPool.Index,
   9332     func_ty: InternPool.Index,
   9333     ret_poison: bool,
   9334     bare_return_type: Type,
   9335     ret_ty_src: LazySrcLoc,
   9336     cc_resolved: std.builtin.CallingConvention,
   9337     ret_ty_requires_comptime: bool,
   9338     func_inst: Zir.Inst.Index,
   9339     cc_src: LazySrcLoc,
   9340     is_noinline: bool,
   9341 ) CompileError!Air.Inst.Ref {
   9342     const pt = sema.pt;
   9343     const zcu = pt.zcu;
   9344     const ip = &zcu.intern_pool;
   9345     const gpa = sema.gpa;
   9346 
   9347     const return_type: Type = if (opt_func_index == .none or ret_poison)
   9348         bare_return_type
   9349     else
   9350         .fromInterned(ip.funcTypeReturnType(ip.typeOf(opt_func_index)));
   9351 
   9352     if (!return_type.isValidReturnType(zcu)) {
   9353         const opaque_str = if (return_type.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
   9354         return sema.fail(block, ret_ty_src, "{s}return type '{f}' not allowed", .{
   9355             opaque_str, return_type.fmt(pt),
   9356         });
   9357     }
   9358     if (!ret_poison and !target_util.fnCallConvAllowsZigTypes(cc_resolved) and
   9359         !try sema.validateExternType(return_type, .ret_ty))
   9360     {
   9361         const msg = msg: {
   9362             const msg = try sema.errMsg(ret_ty_src, "return type '{f}' not allowed in function with calling convention '{s}'", .{
   9363                 return_type.fmt(pt), @tagName(cc_resolved),
   9364             });
   9365             errdefer msg.destroy(gpa);
   9366 
   9367             try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, return_type, .ret_ty);
   9368 
   9369             try sema.addDeclaredHereNote(msg, return_type);
   9370             break :msg msg;
   9371         };
   9372         return sema.failWithOwnedErrorMsg(block, msg);
   9373     }
   9374 
   9375     // If the return type is comptime-only but not dependent on parameters then
   9376     // all parameter types also need to be comptime.
   9377     if (opt_func_index != .none and ret_ty_requires_comptime and !block.isComptime()) comptime_check: {
   9378         for (block.params.items(.is_comptime)) |is_comptime| {
   9379             if (!is_comptime) break;
   9380         } else break :comptime_check;
   9381 
   9382         const msg = try sema.errMsg(
   9383             ret_ty_src,
   9384             "function with comptime-only return type '{f}' requires all parameters to be comptime",
   9385             .{return_type.fmt(pt)},
   9386         );
   9387         errdefer msg.destroy(sema.gpa);
   9388         try sema.explainWhyTypeIsComptime(msg, ret_ty_src, return_type);
   9389 
   9390         const tags = sema.code.instructions.items(.tag);
   9391         const data = sema.code.instructions.items(.data);
   9392         const param_body = sema.code.getParamBody(func_inst);
   9393         for (
   9394             block.params.items(.is_comptime),
   9395             block.params.items(.name),
   9396             param_body[0..block.params.len],
   9397         ) |is_comptime, name_nts, param_index| {
   9398             if (!is_comptime) {
   9399                 const param_src = block.tokenOffset(switch (tags[@intFromEnum(param_index)]) {
   9400                     .param => data[@intFromEnum(param_index)].pl_tok.src_tok,
   9401                     .param_anytype => data[@intFromEnum(param_index)].str_tok.src_tok,
   9402                     else => unreachable,
   9403                 });
   9404                 const name = sema.code.nullTerminatedString(name_nts);
   9405                 if (name.len != 0) {
   9406                     try sema.errNote(param_src, msg, "param '{s}' is required to be comptime", .{name});
   9407                 } else {
   9408                     try sema.errNote(param_src, msg, "param is required to be comptime", .{});
   9409                 }
   9410             }
   9411         }
   9412         return sema.failWithOwnedErrorMsg(block, msg);
   9413     }
   9414 
   9415     validate_incoming_stack_align: {
   9416         const a: u64 = switch (cc_resolved) {
   9417             inline else => |payload| if (@TypeOf(payload) != void and @hasField(@TypeOf(payload), "incoming_stack_alignment"))
   9418                 payload.incoming_stack_alignment orelse break :validate_incoming_stack_align
   9419             else
   9420                 break :validate_incoming_stack_align,
   9421         };
   9422         if (!std.math.isPowerOfTwo(a)) {
   9423             return sema.fail(block, cc_src, "calling convention incoming stack alignment '{d}' is not a power of two", .{a});
   9424         }
   9425     }
   9426 
   9427     switch (cc_resolved) {
   9428         .x86_64_interrupt,
   9429         .x86_interrupt,
   9430         .arm_interrupt,
   9431         .mips64_interrupt,
   9432         .mips_interrupt,
   9433         .riscv64_interrupt,
   9434         .riscv32_interrupt,
   9435         .avr_interrupt,
   9436         .csky_interrupt,
   9437         .m68k_interrupt,
   9438         .avr_signal,
   9439         => if (return_type.zigTypeTag(zcu) != .void and return_type.zigTypeTag(zcu) != .noreturn) {
   9440             return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(cc_resolved)});
   9441         },
   9442         .@"inline" => if (is_noinline) {
   9443             return sema.fail(block, cc_src, "'noinline' function cannot have calling convention 'inline'", .{});
   9444         },
   9445         else => {},
   9446     }
   9447 
   9448     switch (zcu.callconvSupported(cc_resolved)) {
   9449         .ok => {},
   9450         .bad_arch => |allowed_archs| {
   9451             const ArchListFormatter = struct {
   9452                 archs: []const std.Target.Cpu.Arch,
   9453                 pub fn format(formatter: @This(), w: *std.io.Writer) std.io.Writer.Error!void {
   9454                     for (formatter.archs, 0..) |arch, i| {
   9455                         if (i != 0)
   9456                             try w.writeAll(", ");
   9457                         try w.print("'{s}'", .{@tagName(arch)});
   9458                     }
   9459                 }
   9460             };
   9461             return sema.fail(block, cc_src, "calling convention '{s}' only available on architectures {f}", .{
   9462                 @tagName(cc_resolved),
   9463                 ArchListFormatter{ .archs = allowed_archs },
   9464             });
   9465         },
   9466         .bad_backend => |bad_backend| return sema.fail(block, cc_src, "calling convention '{s}' not supported by compiler backend '{s}'", .{
   9467             @tagName(cc_resolved),
   9468             @tagName(bad_backend),
   9469         }),
   9470     }
   9471 
   9472     return Air.internedToRef(if (opt_func_index != .none) opt_func_index else func_ty);
   9473 }
   9474 
   9475 fn zirParam(
   9476     sema: *Sema,
   9477     block: *Block,
   9478     inst: Zir.Inst.Index,
   9479     comptime_syntax: bool,
   9480 ) CompileError!void {
   9481     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
   9482     const src = block.tokenOffset(inst_data.src_tok);
   9483     const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
   9484     const param_name: Zir.NullTerminatedString = extra.data.name;
   9485     const body = sema.code.bodySlice(extra.end, extra.data.type.body_len);
   9486 
   9487     const param_ty: Type = if (extra.data.type.is_generic) .generic_poison else ty: {
   9488         // Make sure any nested param instructions don't clobber our work.
   9489         const prev_params = block.params;
   9490         block.params = .{};
   9491         defer {
   9492             block.params = prev_params;
   9493         }
   9494 
   9495         const param_ty_inst = try sema.resolveInlineBody(block, body, inst);
   9496         break :ty try sema.analyzeAsType(block, src, param_ty_inst);
   9497     };
   9498 
   9499     try block.params.append(sema.arena, .{
   9500         .ty = param_ty.toIntern(),
   9501         .is_comptime = comptime_syntax,
   9502         .name = param_name,
   9503     });
   9504 }
   9505 
   9506 fn zirParamAnytype(
   9507     sema: *Sema,
   9508     block: *Block,
   9509     inst: Zir.Inst.Index,
   9510     comptime_syntax: bool,
   9511 ) CompileError!void {
   9512     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
   9513     const param_name: Zir.NullTerminatedString = inst_data.start;
   9514 
   9515     try block.params.append(sema.arena, .{
   9516         .ty = .generic_poison_type,
   9517         .is_comptime = comptime_syntax,
   9518         .name = param_name,
   9519     });
   9520 }
   9521 
   9522 fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9523     const tracy = trace(@src());
   9524     defer tracy.end();
   9525 
   9526     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9527     const src = block.nodeOffset(inst_data.src_node);
   9528     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
   9529     return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false);
   9530 }
   9531 
   9532 fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9533     const tracy = trace(@src());
   9534     defer tracy.end();
   9535 
   9536     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9537     const src = block.nodeOffset(inst_data.src_node);
   9538     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
   9539     return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true);
   9540 }
   9541 
   9542 fn analyzeAs(
   9543     sema: *Sema,
   9544     block: *Block,
   9545     src: LazySrcLoc,
   9546     zir_dest_type: Zir.Inst.Ref,
   9547     zir_operand: Zir.Inst.Ref,
   9548     no_cast_to_comptime_int: bool,
   9549 ) CompileError!Air.Inst.Ref {
   9550     const pt = sema.pt;
   9551     const zcu = pt.zcu;
   9552     const operand = try sema.resolveInst(zir_operand);
   9553     const dest_ty = try sema.resolveTypeOrPoison(block, src, zir_dest_type) orelse return operand;
   9554     switch (dest_ty.zigTypeTag(zcu)) {
   9555         .@"opaque" => return sema.fail(block, src, "cannot cast to opaque type '{f}'", .{dest_ty.fmt(pt)}),
   9556         .noreturn => return sema.fail(block, src, "cannot cast to noreturn", .{}),
   9557         else => {},
   9558     }
   9559 
   9560     const is_ret = if (zir_dest_type.toIndex()) |ptr_index|
   9561         sema.code.instructions.items(.tag)[@intFromEnum(ptr_index)] == .ret_type
   9562     else
   9563         false;
   9564     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) {
   9565         error.NotCoercible => unreachable,
   9566         else => |e| return e,
   9567     };
   9568 }
   9569 
   9570 fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9571     const tracy = trace(@src());
   9572     defer tracy.end();
   9573 
   9574     const pt = sema.pt;
   9575     const zcu = pt.zcu;
   9576     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
   9577     const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   9578     const operand = try sema.resolveInst(inst_data.operand);
   9579     const operand_ty = sema.typeOf(operand);
   9580     const ptr_ty = operand_ty.scalarType(zcu);
   9581     const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
   9582     if (!ptr_ty.isPtrAtRuntime(zcu)) {
   9583         return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)});
   9584     }
   9585     const pointee_ty = ptr_ty.childType(zcu);
   9586     if (try ptr_ty.comptimeOnlySema(pt)) {
   9587         const msg = msg: {
   9588             const msg = try sema.errMsg(ptr_src, "comptime-only type '{f}' has no pointer address", .{pointee_ty.fmt(pt)});
   9589             errdefer msg.destroy(sema.gpa);
   9590             try sema.explainWhyTypeIsComptime(msg, ptr_src, pointee_ty);
   9591             break :msg msg;
   9592         };
   9593         return sema.failWithOwnedErrorMsg(block, msg);
   9594     }
   9595     const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;
   9596     const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .usize_type, .len = len }) else .usize;
   9597 
   9598     if (try sema.resolveValue(operand)) |operand_val| ct: {
   9599         if (!is_vector) {
   9600             if (operand_val.isUndef(zcu)) {
   9601                 return .undef_usize;
   9602             }
   9603             const addr = try operand_val.getUnsignedIntSema(pt) orelse {
   9604                 // Wasn't an integer pointer. This is a runtime operation.
   9605                 break :ct;
   9606             };
   9607             return Air.internedToRef((try pt.intValue(
   9608                 .usize,
   9609                 addr,
   9610             )).toIntern());
   9611         }
   9612         const new_elems = try sema.arena.alloc(InternPool.Index, len);
   9613         for (new_elems, 0..) |*new_elem, i| {
   9614             const ptr_val = try operand_val.elemValue(pt, i);
   9615             if (ptr_val.isUndef(zcu)) {
   9616                 new_elem.* = .undef_usize;
   9617                 continue;
   9618             }
   9619             const addr = try ptr_val.getUnsignedIntSema(pt) orelse {
   9620                 // A vector element wasn't an integer pointer. This is a runtime operation.
   9621                 break :ct;
   9622             };
   9623             new_elem.* = (try pt.intValue(
   9624                 .usize,
   9625                 addr,
   9626             )).toIntern();
   9627         }
   9628         return Air.internedToRef(try pt.intern(.{ .aggregate = .{
   9629             .ty = dest_ty.toIntern(),
   9630             .storage = .{ .elems = new_elems },
   9631         } }));
   9632     }
   9633     try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src);
   9634     try sema.validateRuntimeValue(block, ptr_src, operand);
   9635     try sema.checkLogicalPtrOperation(block, ptr_src, ptr_ty);
   9636     return block.addBitCast(dest_ty, operand);
   9637 }
   9638 
   9639 fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9640     const tracy = trace(@src());
   9641     defer tracy.end();
   9642 
   9643     const pt = sema.pt;
   9644     const zcu = pt.zcu;
   9645     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9646     const src = block.nodeOffset(inst_data.src_node);
   9647     const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
   9648     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9649     const field_name = try zcu.intern_pool.getOrPutString(
   9650         sema.gpa,
   9651         pt.tid,
   9652         sema.code.nullTerminatedString(extra.field_name_start),
   9653         .no_embedded_nulls,
   9654     );
   9655     const object = try sema.resolveInst(extra.lhs);
   9656     return sema.fieldVal(block, src, object, field_name, field_name_src);
   9657 }
   9658 
   9659 fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9660     const tracy = trace(@src());
   9661     defer tracy.end();
   9662 
   9663     const pt = sema.pt;
   9664     const zcu = pt.zcu;
   9665     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9666     const src = block.nodeOffset(inst_data.src_node);
   9667     const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
   9668     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9669     const field_name = try zcu.intern_pool.getOrPutString(
   9670         sema.gpa,
   9671         pt.tid,
   9672         sema.code.nullTerminatedString(extra.field_name_start),
   9673         .no_embedded_nulls,
   9674     );
   9675     const object_ptr = try sema.resolveInst(extra.lhs);
   9676     return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
   9677 }
   9678 
   9679 fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9680     const tracy = trace(@src());
   9681     defer tracy.end();
   9682 
   9683     const pt = sema.pt;
   9684     const zcu = pt.zcu;
   9685     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9686     const src = block.nodeOffset(inst_data.src_node);
   9687     const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node });
   9688     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
   9689     const field_name = try zcu.intern_pool.getOrPutString(
   9690         sema.gpa,
   9691         pt.tid,
   9692         sema.code.nullTerminatedString(extra.field_name_start),
   9693         .no_embedded_nulls,
   9694     );
   9695     const object_ptr = try sema.resolveInst(extra.lhs);
   9696     const struct_ty = sema.typeOf(object_ptr).childType(zcu);
   9697     switch (struct_ty.zigTypeTag(zcu)) {
   9698         .@"struct", .@"union" => {
   9699             return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, true);
   9700         },
   9701         else => {
   9702             return sema.failWithStructInitNotSupported(block, src, struct_ty);
   9703         },
   9704     }
   9705 }
   9706 
   9707 fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9708     const tracy = trace(@src());
   9709     defer tracy.end();
   9710 
   9711     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9712     const src = block.nodeOffset(inst_data.src_node);
   9713     const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
   9714     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
   9715     const object = try sema.resolveInst(extra.lhs);
   9716     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
   9717     return sema.fieldVal(block, src, object, field_name, field_name_src);
   9718 }
   9719 
   9720 fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9721     const tracy = trace(@src());
   9722     defer tracy.end();
   9723 
   9724     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9725     const src = block.nodeOffset(inst_data.src_node);
   9726     const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
   9727     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
   9728     const object_ptr = try sema.resolveInst(extra.lhs);
   9729     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
   9730     return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
   9731 }
   9732 
   9733 fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9734     const tracy = trace(@src());
   9735     defer tracy.end();
   9736 
   9737     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9738     const src = block.nodeOffset(inst_data.src_node);
   9739     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   9740     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   9741 
   9742     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast");
   9743     const operand = try sema.resolveInst(extra.rhs);
   9744 
   9745     return sema.intCast(block, block.nodeOffset(inst_data.src_node), dest_ty, src, operand, operand_src);
   9746 }
   9747 
   9748 fn intCast(
   9749     sema: *Sema,
   9750     block: *Block,
   9751     src: LazySrcLoc,
   9752     dest_ty: Type,
   9753     dest_ty_src: LazySrcLoc,
   9754     operand: Air.Inst.Ref,
   9755     operand_src: LazySrcLoc,
   9756 ) CompileError!Air.Inst.Ref {
   9757     const pt = sema.pt;
   9758     const zcu = pt.zcu;
   9759     const operand_ty = sema.typeOf(operand);
   9760     const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src);
   9761     const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
   9762 
   9763     if (try sema.isComptimeKnown(operand)) {
   9764         return sema.coerce(block, dest_ty, operand, operand_src);
   9765     } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
   9766         return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
   9767     }
   9768 
   9769     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src);
   9770     const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
   9771 
   9772     if ((try sema.typeHasOnePossibleValue(dest_ty))) |opv| {
   9773         // requirement: intCast(u0, input) iff input == 0
   9774         if (block.wantSafety()) {
   9775             try sema.requireRuntimeBlock(block, src, operand_src);
   9776             const wanted_info = dest_scalar_ty.intInfo(zcu);
   9777             const wanted_bits = wanted_info.bits;
   9778 
   9779             if (wanted_bits == 0) {
   9780                 const ok = if (is_vector) ok: {
   9781                     const zeros = try sema.splat(operand_ty, try pt.intValue(operand_scalar_ty, 0));
   9782                     const zero_inst = Air.internedToRef(zeros.toIntern());
   9783                     const is_in_range = try block.addCmpVector(operand, zero_inst, .eq);
   9784                     const all_in_range = try block.addReduce(is_in_range, .And);
   9785                     break :ok all_in_range;
   9786                 } else ok: {
   9787                     const zero_inst = Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern());
   9788                     const is_in_range = try block.addBinOp(.cmp_lte, operand, zero_inst);
   9789                     break :ok is_in_range;
   9790                 };
   9791                 try sema.addSafetyCheck(block, src, ok, .integer_out_of_bounds);
   9792             }
   9793         }
   9794 
   9795         return Air.internedToRef(opv.toIntern());
   9796     }
   9797 
   9798     try sema.requireRuntimeBlock(block, src, operand_src);
   9799     if (block.wantSafety()) {
   9800         try sema.preparePanicId(src, .integer_out_of_bounds);
   9801         return block.addTyOp(.intcast_safe, dest_ty, operand);
   9802     }
   9803     return block.addTyOp(.intcast, dest_ty, operand);
   9804 }
   9805 
   9806 fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9807     const tracy = trace(@src());
   9808     defer tracy.end();
   9809 
   9810     const pt = sema.pt;
   9811     const zcu = pt.zcu;
   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, "@bitCast");
   9818     const operand = try sema.resolveInst(extra.rhs);
   9819     const operand_ty = sema.typeOf(operand);
   9820     switch (dest_ty.zigTypeTag(zcu)) {
   9821         .@"anyframe",
   9822         .comptime_float,
   9823         .comptime_int,
   9824         .enum_literal,
   9825         .error_set,
   9826         .error_union,
   9827         .@"fn",
   9828         .frame,
   9829         .noreturn,
   9830         .null,
   9831         .@"opaque",
   9832         .optional,
   9833         .type,
   9834         .undefined,
   9835         .void,
   9836         => return sema.fail(block, src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}),
   9837 
   9838         .@"enum" => {
   9839             const msg = msg: {
   9840                 const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)});
   9841                 errdefer msg.destroy(sema.gpa);
   9842                 switch (operand_ty.zigTypeTag(zcu)) {
   9843                     .int, .comptime_int => try sema.errNote(src, msg, "use @enumFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}),
   9844                     else => {},
   9845                 }
   9846 
   9847                 break :msg msg;
   9848             };
   9849             return sema.failWithOwnedErrorMsg(block, msg);
   9850         },
   9851 
   9852         .pointer => {
   9853             const msg = msg: {
   9854                 const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)});
   9855                 errdefer msg.destroy(sema.gpa);
   9856                 switch (operand_ty.zigTypeTag(zcu)) {
   9857                     .int, .comptime_int => try sema.errNote(src, msg, "use @ptrFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}),
   9858                     .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}),
   9859                     else => {},
   9860                 }
   9861 
   9862                 break :msg msg;
   9863             };
   9864             return sema.failWithOwnedErrorMsg(block, msg);
   9865         },
   9866         .@"struct", .@"union" => if (dest_ty.containerLayout(zcu) == .auto) {
   9867             const container = switch (dest_ty.zigTypeTag(zcu)) {
   9868                 .@"struct" => "struct",
   9869                 .@"union" => "union",
   9870                 else => unreachable,
   9871             };
   9872             return sema.fail(block, src, "cannot @bitCast to '{f}'; {s} does not have a guaranteed in-memory layout", .{
   9873                 dest_ty.fmt(pt), container,
   9874             });
   9875         },
   9876 
   9877         .array,
   9878         .bool,
   9879         .float,
   9880         .int,
   9881         .vector,
   9882         => {},
   9883     }
   9884     switch (operand_ty.zigTypeTag(zcu)) {
   9885         .@"anyframe",
   9886         .comptime_float,
   9887         .comptime_int,
   9888         .enum_literal,
   9889         .error_set,
   9890         .error_union,
   9891         .@"fn",
   9892         .frame,
   9893         .noreturn,
   9894         .null,
   9895         .@"opaque",
   9896         .optional,
   9897         .type,
   9898         .undefined,
   9899         .void,
   9900         => return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}),
   9901 
   9902         .@"enum" => {
   9903             const msg = msg: {
   9904                 const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});
   9905                 errdefer msg.destroy(sema.gpa);
   9906                 switch (dest_ty.zigTypeTag(zcu)) {
   9907                     .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromEnum to cast to '{f}'", .{dest_ty.fmt(pt)}),
   9908                     else => {},
   9909                 }
   9910 
   9911                 break :msg msg;
   9912             };
   9913             return sema.failWithOwnedErrorMsg(block, msg);
   9914         },
   9915         .pointer => {
   9916             const msg = msg: {
   9917                 const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});
   9918                 errdefer msg.destroy(sema.gpa);
   9919                 switch (dest_ty.zigTypeTag(zcu)) {
   9920                     .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromPtr to cast to '{f}'", .{dest_ty.fmt(pt)}),
   9921                     .pointer => try sema.errNote(operand_src, msg, "use @ptrCast to cast to '{f}'", .{dest_ty.fmt(pt)}),
   9922                     else => {},
   9923                 }
   9924 
   9925                 break :msg msg;
   9926             };
   9927             return sema.failWithOwnedErrorMsg(block, msg);
   9928         },
   9929         .@"struct", .@"union" => if (operand_ty.containerLayout(zcu) == .auto) {
   9930             const container = switch (operand_ty.zigTypeTag(zcu)) {
   9931                 .@"struct" => "struct",
   9932                 .@"union" => "union",
   9933                 else => unreachable,
   9934             };
   9935             return sema.fail(block, operand_src, "cannot @bitCast from '{f}'; {s} does not have a guaranteed in-memory layout", .{
   9936                 operand_ty.fmt(pt), container,
   9937             });
   9938         },
   9939 
   9940         .array,
   9941         .bool,
   9942         .float,
   9943         .int,
   9944         .vector,
   9945         => {},
   9946     }
   9947     return sema.bitCast(block, dest_ty, operand, block.nodeOffset(inst_data.src_node), operand_src);
   9948 }
   9949 
   9950 fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
   9951     const tracy = trace(@src());
   9952     defer tracy.end();
   9953 
   9954     const pt = sema.pt;
   9955     const zcu = pt.zcu;
   9956     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
   9957     const src = block.nodeOffset(inst_data.src_node);
   9958     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
   9959     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
   9960 
   9961     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast");
   9962     const dest_scalar_ty = dest_ty.scalarType(zcu);
   9963 
   9964     const operand = try sema.resolveInst(extra.rhs);
   9965     const operand_ty = sema.typeOf(operand);
   9966     const operand_scalar_ty = operand_ty.scalarType(zcu);
   9967 
   9968     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
   9969     const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
   9970 
   9971     const target = zcu.getTarget();
   9972     const dest_is_comptime_float = switch (dest_scalar_ty.zigTypeTag(zcu)) {
   9973         .comptime_float => true,
   9974         .float => false,
   9975         else => return sema.fail(
   9976             block,
   9977             src,
   9978             "expected float or vector type, found '{f}'",
   9979             .{dest_ty.fmt(pt)},
   9980         ),
   9981     };
   9982 
   9983     switch (operand_scalar_ty.zigTypeTag(zcu)) {
   9984         .comptime_float, .float, .comptime_int => {},
   9985         else => return sema.fail(
   9986             block,
   9987             operand_src,
   9988             "expected float or vector type, found '{f}'",
   9989             .{operand_ty.fmt(pt)},
   9990         ),
   9991     }
   9992 
   9993     if (try sema.resolveValue(operand)) |operand_val| {
   9994         if (!is_vector) {
   9995             return Air.internedToRef((try operand_val.floatCast(dest_ty, pt)).toIntern());
   9996         }
   9997         const vec_len = operand_ty.vectorLen(zcu);
   9998         const new_elems = try sema.arena.alloc(InternPool.Index, vec_len);
   9999         for (new_elems, 0..) |*new_elem, i| {
  10000             const old_elem = try operand_val.elemValue(pt, i);
  10001             new_elem.* = (try old_elem.floatCast(dest_scalar_ty, pt)).toIntern();
  10002         }
  10003         return Air.internedToRef(try pt.intern(.{ .aggregate = .{
  10004             .ty = dest_ty.toIntern(),
  10005             .storage = .{ .elems = new_elems },
  10006         } }));
  10007     }
  10008     if (dest_is_comptime_float) {
  10009         return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{});
  10010     }
  10011     try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src);
  10012 
  10013     const src_bits = operand_scalar_ty.floatBits(target);
  10014     const dst_bits = dest_scalar_ty.floatBits(target);
  10015     if (dst_bits >= src_bits) {
  10016         return sema.coerce(block, dest_ty, operand, operand_src);
  10017     }
  10018     return block.addTyOp(.fptrunc, dest_ty, operand);
  10019 }
  10020 
  10021 fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10022     const tracy = trace(@src());
  10023     defer tracy.end();
  10024 
  10025     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10026     const src = block.nodeOffset(inst_data.src_node);
  10027     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10028     const array = try sema.resolveInst(extra.lhs);
  10029     const elem_index = try sema.resolveInst(extra.rhs);
  10030     return sema.elemVal(block, src, array, elem_index, src, false);
  10031 }
  10032 
  10033 fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10034     const tracy = trace(@src());
  10035     defer tracy.end();
  10036 
  10037     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10038     const src = block.nodeOffset(inst_data.src_node);
  10039     const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node });
  10040     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10041     const array = try sema.resolveInst(extra.lhs);
  10042     const uncoerced_elem_index = try sema.resolveInst(extra.rhs);
  10043     const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src);
  10044     return sema.elemVal(block, src, array, elem_index, elem_index_src, true);
  10045 }
  10046 
  10047 fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10048     const tracy = trace(@src());
  10049     defer tracy.end();
  10050 
  10051     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm;
  10052     const array = try sema.resolveInst(inst_data.operand);
  10053     const elem_index = try sema.pt.intRef(.usize, inst_data.idx);
  10054     return sema.elemVal(block, LazySrcLoc.unneeded, array, elem_index, LazySrcLoc.unneeded, false);
  10055 }
  10056 
  10057 fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10058     const tracy = trace(@src());
  10059     defer tracy.end();
  10060 
  10061     const pt = sema.pt;
  10062     const zcu = pt.zcu;
  10063     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10064     const src = block.nodeOffset(inst_data.src_node);
  10065     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10066     const array_ptr = try sema.resolveInst(extra.lhs);
  10067     const elem_index = try sema.resolveInst(extra.rhs);
  10068     const indexable_ty = sema.typeOf(array_ptr);
  10069     if (indexable_ty.zigTypeTag(zcu) != .pointer) {
  10070         const capture_src = block.src(.{ .for_capture_from_input = inst_data.src_node });
  10071         const msg = msg: {
  10072             const msg = try sema.errMsg(capture_src, "pointer capture of non pointer type '{f}'", .{
  10073                 indexable_ty.fmt(pt),
  10074             });
  10075             errdefer msg.destroy(sema.gpa);
  10076             if (indexable_ty.isIndexable(zcu)) {
  10077                 try sema.errNote(src, msg, "consider using '&' here", .{});
  10078             }
  10079             break :msg msg;
  10080         };
  10081         return sema.failWithOwnedErrorMsg(block, msg);
  10082     }
  10083     return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false);
  10084 }
  10085 
  10086 fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10087     const tracy = trace(@src());
  10088     defer tracy.end();
  10089 
  10090     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10091     const src = block.nodeOffset(inst_data.src_node);
  10092     const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node });
  10093     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  10094     const array_ptr = try sema.resolveInst(extra.lhs);
  10095     const uncoerced_elem_index = try sema.resolveInst(extra.rhs);
  10096     const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src);
  10097     return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true);
  10098 }
  10099 
  10100 fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10101     const tracy = trace(@src());
  10102     defer tracy.end();
  10103 
  10104     const pt = sema.pt;
  10105     const zcu = pt.zcu;
  10106     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10107     const src = block.nodeOffset(inst_data.src_node);
  10108     const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
  10109     const array_ptr = try sema.resolveInst(extra.ptr);
  10110     const elem_index = try pt.intRef(.usize, extra.index);
  10111     const array_ty = sema.typeOf(array_ptr).childType(zcu);
  10112     switch (array_ty.zigTypeTag(zcu)) {
  10113         .array, .vector => {},
  10114         else => if (!array_ty.isTuple(zcu)) {
  10115             return sema.failWithArrayInitNotSupported(block, src, array_ty);
  10116         },
  10117     }
  10118     return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true);
  10119 }
  10120 
  10121 fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10122     const tracy = trace(@src());
  10123     defer tracy.end();
  10124 
  10125     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10126     const src = block.nodeOffset(inst_data.src_node);
  10127     const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
  10128     const array_ptr = try sema.resolveInst(extra.lhs);
  10129     const start = try sema.resolveInst(extra.start);
  10130     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10131     const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node });
  10132     const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
  10133 
  10134     return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false);
  10135 }
  10136 
  10137 fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10138     const tracy = trace(@src());
  10139     defer tracy.end();
  10140 
  10141     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10142     const src = block.nodeOffset(inst_data.src_node);
  10143     const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
  10144     const array_ptr = try sema.resolveInst(extra.lhs);
  10145     const start = try sema.resolveInst(extra.start);
  10146     const end = try sema.resolveInst(extra.end);
  10147     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10148     const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node });
  10149     const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
  10150 
  10151     return sema.analyzeSlice(block, src, array_ptr, start, end, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false);
  10152 }
  10153 
  10154 fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10155     const tracy = trace(@src());
  10156     defer tracy.end();
  10157 
  10158     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10159     const src = block.nodeOffset(inst_data.src_node);
  10160     const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
  10161     const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
  10162     const array_ptr = try sema.resolveInst(extra.lhs);
  10163     const start = try sema.resolveInst(extra.start);
  10164     const end: Air.Inst.Ref = if (extra.end == .none) .none else try sema.resolveInst(extra.end);
  10165     const sentinel = try sema.resolveInst(extra.sentinel);
  10166     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10167     const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node });
  10168     const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
  10169 
  10170     return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false);
  10171 }
  10172 
  10173 fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10174     const tracy = trace(@src());
  10175     defer tracy.end();
  10176 
  10177     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10178     const src = block.nodeOffset(inst_data.src_node);
  10179     const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
  10180     const array_ptr = try sema.resolveInst(extra.lhs);
  10181     const start = try sema.resolveInst(extra.start);
  10182     const len = try sema.resolveInst(extra.len);
  10183     const sentinel = if (extra.sentinel == .none) .none else try sema.resolveInst(extra.sentinel);
  10184     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10185     const start_src = block.src(.{ .node_offset_slice_start = extra.start_src_node_offset });
  10186     const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
  10187     const sentinel_src: LazySrcLoc = if (sentinel == .none)
  10188         LazySrcLoc.unneeded
  10189     else
  10190         block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
  10191 
  10192     return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
  10193 }
  10194 
  10195 fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10196     const tracy = trace(@src());
  10197     defer tracy.end();
  10198 
  10199     const pt = sema.pt;
  10200     const zcu = pt.zcu;
  10201 
  10202     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  10203 
  10204     const src = block.nodeOffset(inst_data.src_node);
  10205     const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
  10206     const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
  10207 
  10208     // This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will
  10209     // have a double pointer if it was already a pointer.
  10210 
  10211     const lhs_ptr_ty = sema.typeOf(try sema.resolveInst(inst_data.operand));
  10212     const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) {
  10213         .pointer => lhs_ptr_ty.childType(zcu),
  10214         else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{lhs_ptr_ty.fmt(pt)}),
  10215     };
  10216 
  10217     const sentinel_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
  10218         .array => lhs_ty.childType(zcu),
  10219         .pointer => switch (lhs_ty.ptrSize(zcu)) {
  10220             .many, .c, .slice => lhs_ty.childType(zcu),
  10221             .one => s: {
  10222                 const lhs_elem_ty = lhs_ty.childType(zcu);
  10223                 break :s switch (lhs_elem_ty.zigTypeTag(zcu)) {
  10224                     .array => lhs_elem_ty.childType(zcu), // array element type
  10225                     else => return sema.fail(block, sentinel_src, "slice of single-item pointer cannot have sentinel", .{}),
  10226                 };
  10227             },
  10228         },
  10229         else => return sema.fail(block, src, "slice of non-array type '{f}'", .{lhs_ty.fmt(pt)}),
  10230     };
  10231 
  10232     return Air.internedToRef(sentinel_ty.toIntern());
  10233 }
  10234 
  10235 /// Holds common data used when analyzing or resolving switch prong bodies,
  10236 /// including setting up captures.
  10237 const SwitchProngAnalysis = struct {
  10238     sema: *Sema,
  10239     /// The block containing the `switch_block` itself.
  10240     parent_block: *Block,
  10241     operand: Operand,
  10242     /// If this switch is on an error set, this is the type to assign to the
  10243     /// `else` prong. If `null`, the prong should be unreachable.
  10244     else_error_ty: ?Type,
  10245     /// The index of the `switch_block` instruction itself.
  10246     switch_block_inst: Zir.Inst.Index,
  10247     /// The dummy index into which inline tag captures should be placed. May be
  10248     /// undefined if no prong has a tag capture.
  10249     tag_capture_inst: Zir.Inst.Index,
  10250 
  10251     const Operand = union(enum) {
  10252         /// This switch will be dispatched only once, with the given operand.
  10253         simple: struct {
  10254             /// The raw switch operand value. Always defined.
  10255             by_val: Air.Inst.Ref,
  10256             /// The switch operand *pointer*. Defined only if there is a prong
  10257             /// with a by-ref capture.
  10258             by_ref: Air.Inst.Ref,
  10259             /// The switch condition value. For unions, `operand` is the union
  10260             /// and `cond` is its enum tag value.
  10261             cond: Air.Inst.Ref,
  10262         },
  10263         /// This switch may be dispatched multiple times with `continue` syntax.
  10264         /// As such, the operand is stored in an alloc if needed.
  10265         loop: struct {
  10266             /// The `alloc` containing the `switch` operand for the active dispatch.
  10267             /// Each prong must load from this `alloc` to get captures.
  10268             /// If there are no captures, this may be undefined.
  10269             operand_alloc: Air.Inst.Ref,
  10270             /// Whether `operand_alloc` contains a by-val operand or a by-ref
  10271             /// operand.
  10272             operand_is_ref: bool,
  10273             /// The switch condition value for the *initial* dispatch. For
  10274             /// unions, this is the enum tag value.
  10275             init_cond: Air.Inst.Ref,
  10276         },
  10277     };
  10278 
  10279     /// Resolve a switch prong which is determined at comptime to have no peers.
  10280     /// Uses `resolveBlockBody`. Sets up captures as needed.
  10281     fn resolveProngComptime(
  10282         spa: SwitchProngAnalysis,
  10283         child_block: *Block,
  10284         prong_type: enum { normal, special },
  10285         prong_body: []const Zir.Inst.Index,
  10286         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10287         /// Must use the `switch_capture` field in `offset`.
  10288         capture_src: LazySrcLoc,
  10289         /// The set of all values which can reach this prong. May be undefined
  10290         /// if the prong is special or contains ranges.
  10291         case_vals: []const Air.Inst.Ref,
  10292         /// The inline capture of this prong. If this is not an inline prong,
  10293         /// this is `.none`.
  10294         inline_case_capture: Air.Inst.Ref,
  10295         /// Whether this prong has an inline tag capture. If `true`, then
  10296         /// `inline_case_capture` cannot be `.none`.
  10297         has_tag_capture: bool,
  10298         merges: *Block.Merges,
  10299     ) CompileError!Air.Inst.Ref {
  10300         const sema = spa.sema;
  10301         const src = spa.parent_block.nodeOffset(
  10302             sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src_node,
  10303         );
  10304 
  10305         // We can propagate `.cold` hints from this branch since it's comptime-known
  10306         // to be taken from the parent branch.
  10307         const parent_hint = sema.branch_hint;
  10308         defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
  10309 
  10310         if (has_tag_capture) {
  10311             const tag_ref = try spa.analyzeTagCapture(child_block, capture_src, inline_case_capture);
  10312             sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
  10313         }
  10314         defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
  10315 
  10316         switch (capture) {
  10317             .none => {
  10318                 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
  10319             },
  10320 
  10321             .by_val, .by_ref => {
  10322                 const capture_ref = try spa.analyzeCapture(
  10323                     child_block,
  10324                     capture == .by_ref,
  10325                     prong_type == .special,
  10326                     capture_src,
  10327                     case_vals,
  10328                     inline_case_capture,
  10329                 );
  10330 
  10331                 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) {
  10332                     // This prong should be unreachable!
  10333                     return .unreachable_value;
  10334                 }
  10335 
  10336                 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
  10337                 defer assert(sema.inst_map.remove(spa.switch_block_inst));
  10338 
  10339                 return sema.resolveBlockBody(spa.parent_block, src, child_block, prong_body, spa.switch_block_inst, merges);
  10340             },
  10341         }
  10342     }
  10343 
  10344     /// Analyze a switch prong which may have peers at runtime.
  10345     /// Uses `analyzeBodyRuntimeBreak`. Sets up captures as needed.
  10346     /// Returns the `BranchHint` for the prong.
  10347     fn analyzeProngRuntime(
  10348         spa: SwitchProngAnalysis,
  10349         case_block: *Block,
  10350         prong_type: enum { normal, special },
  10351         prong_body: []const Zir.Inst.Index,
  10352         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10353         /// Must use the `switch_capture` field in `offset`.
  10354         capture_src: LazySrcLoc,
  10355         /// The set of all values which can reach this prong. May be undefined
  10356         /// if the prong is special or contains ranges.
  10357         case_vals: []const Air.Inst.Ref,
  10358         /// The inline capture of this prong. If this is not an inline prong,
  10359         /// this is `.none`.
  10360         inline_case_capture: Air.Inst.Ref,
  10361         /// Whether this prong has an inline tag capture. If `true`, then
  10362         /// `inline_case_capture` cannot be `.none`.
  10363         has_tag_capture: bool,
  10364     ) CompileError!std.builtin.BranchHint {
  10365         const sema = spa.sema;
  10366 
  10367         if (has_tag_capture) {
  10368             const tag_ref = try spa.analyzeTagCapture(case_block, capture_src, inline_case_capture);
  10369             sema.inst_map.putAssumeCapacity(spa.tag_capture_inst, tag_ref);
  10370         }
  10371         defer if (has_tag_capture) assert(sema.inst_map.remove(spa.tag_capture_inst));
  10372 
  10373         switch (capture) {
  10374             .none => {
  10375                 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
  10376             },
  10377 
  10378             .by_val, .by_ref => {
  10379                 const capture_ref = try spa.analyzeCapture(
  10380                     case_block,
  10381                     capture == .by_ref,
  10382                     prong_type == .special,
  10383                     capture_src,
  10384                     case_vals,
  10385                     inline_case_capture,
  10386                 );
  10387 
  10388                 if (sema.typeOf(capture_ref).isNoReturn(sema.pt.zcu)) {
  10389                     // No need to analyze any further, the prong is unreachable
  10390                     return .none;
  10391                 }
  10392 
  10393                 sema.inst_map.putAssumeCapacity(spa.switch_block_inst, capture_ref);
  10394                 defer assert(sema.inst_map.remove(spa.switch_block_inst));
  10395 
  10396                 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
  10397             },
  10398         }
  10399     }
  10400 
  10401     fn analyzeTagCapture(
  10402         spa: SwitchProngAnalysis,
  10403         block: *Block,
  10404         capture_src: LazySrcLoc,
  10405         inline_case_capture: Air.Inst.Ref,
  10406     ) CompileError!Air.Inst.Ref {
  10407         const sema = spa.sema;
  10408         const pt = sema.pt;
  10409         const zcu = pt.zcu;
  10410         const operand_ty = switch (spa.operand) {
  10411             .simple => |s| sema.typeOf(s.by_val),
  10412             .loop => |l| ty: {
  10413                 const alloc_ty = sema.typeOf(l.operand_alloc);
  10414                 const alloc_child = alloc_ty.childType(zcu);
  10415                 if (l.operand_is_ref) break :ty alloc_child.childType(zcu);
  10416                 break :ty alloc_child;
  10417             },
  10418         };
  10419         if (operand_ty.zigTypeTag(zcu) != .@"union") {
  10420             const tag_capture_src: LazySrcLoc = .{
  10421                 .base_node_inst = capture_src.base_node_inst,
  10422                 .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture },
  10423             };
  10424             return sema.fail(block, tag_capture_src, "cannot capture tag of non-union type '{f}'", .{
  10425                 operand_ty.fmt(pt),
  10426             });
  10427         }
  10428         assert(inline_case_capture != .none);
  10429         return inline_case_capture;
  10430     }
  10431 
  10432     fn analyzeCapture(
  10433         spa: SwitchProngAnalysis,
  10434         block: *Block,
  10435         capture_byref: bool,
  10436         is_special_prong: bool,
  10437         capture_src: LazySrcLoc,
  10438         case_vals: []const Air.Inst.Ref,
  10439         inline_case_capture: Air.Inst.Ref,
  10440     ) CompileError!Air.Inst.Ref {
  10441         const sema = spa.sema;
  10442         const pt = sema.pt;
  10443         const zcu = pt.zcu;
  10444         const ip = &zcu.intern_pool;
  10445 
  10446         const zir_datas = sema.code.instructions.items(.data);
  10447         const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node;
  10448 
  10449         const operand_src = block.src(.{ .node_offset_switch_operand = switch_node_offset });
  10450 
  10451         const operand_val, const operand_ptr = switch (spa.operand) {
  10452             .simple => |s| .{ s.by_val, s.by_ref },
  10453             .loop => |l| op: {
  10454                 const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src);
  10455                 if (l.operand_is_ref) {
  10456                     const by_val = try sema.analyzeLoad(block, operand_src, loaded, operand_src);
  10457                     break :op .{ by_val, loaded };
  10458                 } else {
  10459                     break :op .{ loaded, undefined };
  10460                 }
  10461             },
  10462         };
  10463 
  10464         const operand_ty = sema.typeOf(operand_val);
  10465         const operand_ptr_ty = if (capture_byref) sema.typeOf(operand_ptr) else undefined;
  10466 
  10467         if (inline_case_capture != .none) {
  10468             const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inline_case_capture, undefined) catch unreachable;
  10469             if (operand_ty.zigTypeTag(zcu) == .@"union") {
  10470                 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, zcu).?);
  10471                 const union_obj = zcu.typeToUnion(operand_ty).?;
  10472                 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  10473                 if (capture_byref) {
  10474                     const ptr_field_ty = try pt.ptrTypeSema(.{
  10475                         .child = field_ty.toIntern(),
  10476                         .flags = .{
  10477                             .is_const = !operand_ptr_ty.ptrIsMutable(zcu),
  10478                             .is_volatile = operand_ptr_ty.isVolatilePtr(zcu),
  10479                             .address_space = operand_ptr_ty.ptrAddressSpace(zcu),
  10480                         },
  10481                     });
  10482                     if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |union_ptr| {
  10483                         return Air.internedToRef((try union_ptr.ptrField(field_index, pt)).toIntern());
  10484                     }
  10485                     return block.addStructFieldPtr(operand_ptr, field_index, ptr_field_ty);
  10486                 } else {
  10487                     if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |union_val| {
  10488                         const tag_and_val = ip.indexToKey(union_val.toIntern()).un;
  10489                         return Air.internedToRef(tag_and_val.val);
  10490                     }
  10491                     return block.addStructFieldVal(operand_val, field_index, field_ty);
  10492                 }
  10493             } else if (capture_byref) {
  10494                 return sema.uavRef(item_val.toIntern());
  10495             } else {
  10496                 return inline_case_capture;
  10497             }
  10498         }
  10499 
  10500         if (is_special_prong) {
  10501             if (capture_byref) {
  10502                 return operand_ptr;
  10503             }
  10504 
  10505             switch (operand_ty.zigTypeTag(zcu)) {
  10506                 .error_set => if (spa.else_error_ty) |ty| {
  10507                     return sema.bitCast(block, ty, operand_val, operand_src, null);
  10508                 } else {
  10509                     try sema.analyzeUnreachable(block, operand_src, false);
  10510                     return .unreachable_value;
  10511                 },
  10512                 else => return operand_val,
  10513             }
  10514         }
  10515 
  10516         switch (operand_ty.zigTypeTag(zcu)) {
  10517             .@"union" => {
  10518                 const union_obj = zcu.typeToUnion(operand_ty).?;
  10519                 const first_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable;
  10520 
  10521                 const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?;
  10522                 const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]);
  10523 
  10524                 const field_indices = try sema.arena.alloc(u32, case_vals.len);
  10525                 for (case_vals, field_indices) |item, *field_idx| {
  10526                     const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  10527                     field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?;
  10528                 }
  10529 
  10530                 // Fast path: if all the operands are the same type already, we don't need to hit
  10531                 // PTR! This will also allow us to emit simpler code.
  10532                 const same_types = for (field_indices[1..]) |field_idx| {
  10533                     const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10534                     if (!field_ty.eql(first_field_ty, zcu)) break false;
  10535                 } else true;
  10536 
  10537                 const capture_ty = if (same_types) first_field_ty else capture_ty: {
  10538                     // We need values to run PTR on, so make a bunch of undef constants.
  10539                     const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
  10540                     for (dummy_captures, field_indices) |*dummy, field_idx| {
  10541                         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10542                         dummy.* = try pt.undefRef(field_ty);
  10543                     }
  10544 
  10545                     const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
  10546                     for (case_srcs, 0..) |*case_src, i| {
  10547                         case_src.* = .{
  10548                             .base_node_inst = capture_src.base_node_inst,
  10549                             .offset = .{ .switch_case_item = .{
  10550                                 .switch_node_offset = switch_node_offset,
  10551                                 .case_idx = capture_src.offset.switch_capture.case_idx,
  10552                                 .item_idx = .{ .kind = .single, .index = @intCast(i) },
  10553                             } },
  10554                         };
  10555                     }
  10556 
  10557                     break :capture_ty sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
  10558                         error.AnalysisFail => {
  10559                             const msg = sema.err orelse return error.AnalysisFail;
  10560                             try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
  10561                             return error.AnalysisFail;
  10562                         },
  10563                         else => |e| return e,
  10564                     };
  10565                 };
  10566 
  10567                 // By-reference captures have some further restrictions which make them easier to emit
  10568                 if (capture_byref) {
  10569                     const operand_ptr_info = operand_ptr_ty.ptrInfo(zcu);
  10570                     const capture_ptr_ty = resolve: {
  10571                         // By-ref captures of hetereogeneous types are only allowed if all field
  10572                         // pointer types are peer resolvable to each other.
  10573                         // We need values to run PTR on, so make a bunch of undef constants.
  10574                         const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, case_vals.len);
  10575                         for (field_indices, dummy_captures) |field_idx, *dummy| {
  10576                             const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10577                             const field_ptr_ty = try pt.ptrTypeSema(.{
  10578                                 .child = field_ty.toIntern(),
  10579                                 .flags = .{
  10580                                     .is_const = operand_ptr_info.flags.is_const,
  10581                                     .is_volatile = operand_ptr_info.flags.is_volatile,
  10582                                     .address_space = operand_ptr_info.flags.address_space,
  10583                                     .alignment = union_obj.fieldAlign(ip, field_idx),
  10584                                 },
  10585                             });
  10586                             dummy.* = try pt.undefRef(field_ptr_ty);
  10587                         }
  10588                         const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len);
  10589                         for (case_srcs, 0..) |*case_src, i| {
  10590                             case_src.* = .{
  10591                                 .base_node_inst = capture_src.base_node_inst,
  10592                                 .offset = .{ .switch_case_item = .{
  10593                                     .switch_node_offset = switch_node_offset,
  10594                                     .case_idx = capture_src.offset.switch_capture.case_idx,
  10595                                     .item_idx = .{ .kind = .single, .index = @intCast(i) },
  10596                                 } },
  10597                             };
  10598                         }
  10599 
  10600                         break :resolve sema.resolvePeerTypes(block, capture_src, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) {
  10601                             error.AnalysisFail => {
  10602                                 const msg = sema.err orelse return error.AnalysisFail;
  10603                                 try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{});
  10604                                 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
  10605                                 return error.AnalysisFail;
  10606                             },
  10607                             else => |e| return e,
  10608                         };
  10609                     };
  10610 
  10611                     if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| {
  10612                         if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty);
  10613                         const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt);
  10614                         return Air.internedToRef((try pt.getCoerced(field_ptr_val, capture_ptr_ty)).toIntern());
  10615                     }
  10616 
  10617                     try sema.requireRuntimeBlock(block, operand_src, null);
  10618                     return block.addStructFieldPtr(operand_ptr, first_field_index, capture_ptr_ty);
  10619                 }
  10620 
  10621                 if (try sema.resolveDefinedValue(block, operand_src, operand_val)) |operand_val_val| {
  10622                     if (operand_val_val.isUndef(zcu)) return pt.undefRef(capture_ty);
  10623                     const union_val = ip.indexToKey(operand_val_val.toIntern()).un;
  10624                     if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty);
  10625                     const uncoerced = Air.internedToRef(union_val.val);
  10626                     return sema.coerce(block, capture_ty, uncoerced, operand_src);
  10627                 }
  10628 
  10629                 try sema.requireRuntimeBlock(block, operand_src, null);
  10630 
  10631                 if (same_types) {
  10632                     return block.addStructFieldVal(operand_val, first_field_index, capture_ty);
  10633                 }
  10634 
  10635                 // We may have to emit a switch block which coerces the operand to the capture type.
  10636                 // If we can, try to avoid that using in-memory coercions.
  10637                 const first_non_imc = in_mem: {
  10638                     for (field_indices, 0..) |field_idx, i| {
  10639                         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10640                         if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
  10641                             break :in_mem i;
  10642                         }
  10643                     }
  10644                     // All fields are in-memory coercible to the resolved type!
  10645                     // Just take the first field and bitcast the result.
  10646                     const uncoerced = try block.addStructFieldVal(operand_val, first_field_index, first_field_ty);
  10647                     return block.addBitCast(capture_ty, uncoerced);
  10648                 };
  10649 
  10650                 // By-val capture with heterogeneous types which are not all in-memory coercible to
  10651                 // the resolved capture type. We finally have to fall back to the ugly method.
  10652 
  10653                 // However, let's first track which operands are in-memory coercible. There may well
  10654                 // be several, and we can squash all of these cases into the same switch prong using
  10655                 // a simple bitcast. We'll make this the 'else' prong.
  10656 
  10657                 var in_mem_coercible = try std.DynamicBitSet.initFull(sema.arena, field_indices.len);
  10658                 in_mem_coercible.unset(first_non_imc);
  10659                 {
  10660                     const next = first_non_imc + 1;
  10661                     for (field_indices[next..], next..) |field_idx, i| {
  10662                         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10663                         if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, zcu.getTarget(), LazySrcLoc.unneeded, LazySrcLoc.unneeded, null)) {
  10664                             in_mem_coercible.unset(i);
  10665                         }
  10666                     }
  10667                 }
  10668 
  10669                 const capture_block_inst = try block.addInstAsIndex(.{
  10670                     .tag = .block,
  10671                     .data = .{
  10672                         .ty_pl = .{
  10673                             .ty = Air.internedToRef(capture_ty.toIntern()),
  10674                             .payload = undefined, // updated below
  10675                         },
  10676                     },
  10677                 });
  10678 
  10679                 const prong_count = field_indices.len - in_mem_coercible.count();
  10680 
  10681                 const estimated_extra = prong_count * 6 + (prong_count / 10); // 2 for Case, 1 item, probably 3 insts; plus hints
  10682                 var cases_extra = try std.ArrayList(u32).initCapacity(sema.gpa, estimated_extra);
  10683                 defer cases_extra.deinit();
  10684 
  10685                 {
  10686                     // All branch hints are `.none`, so just add zero elems.
  10687                     comptime assert(@intFromEnum(std.builtin.BranchHint.none) == 0);
  10688                     const need_elems = std.math.divCeil(usize, prong_count + 1, 10) catch unreachable;
  10689                     try cases_extra.appendNTimes(0, need_elems);
  10690                 }
  10691 
  10692                 {
  10693                     // Non-bitcast cases
  10694                     var it = in_mem_coercible.iterator(.{ .kind = .unset });
  10695                     while (it.next()) |idx| {
  10696                         var coerce_block = block.makeSubBlock();
  10697                         defer coerce_block.instructions.deinit(sema.gpa);
  10698 
  10699                         const case_src: LazySrcLoc = .{
  10700                             .base_node_inst = capture_src.base_node_inst,
  10701                             .offset = .{ .switch_case_item = .{
  10702                                 .switch_node_offset = switch_node_offset,
  10703                                 .case_idx = capture_src.offset.switch_capture.case_idx,
  10704                                 .item_idx = .{ .kind = .single, .index = @intCast(idx) },
  10705                             } },
  10706                         };
  10707 
  10708                         const field_idx = field_indices[idx];
  10709                         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
  10710                         const uncoerced = try coerce_block.addStructFieldVal(operand_val, field_idx, field_ty);
  10711                         const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
  10712                         _ = try coerce_block.addBr(capture_block_inst, coerced);
  10713 
  10714                         try cases_extra.ensureUnusedCapacity(@typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  10715                             1 + // `item`, no ranges
  10716                             coerce_block.instructions.items.len);
  10717                         cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  10718                             .items_len = 1,
  10719                             .ranges_len = 0,
  10720                             .body_len = @intCast(coerce_block.instructions.items.len),
  10721                         }));
  10722                         cases_extra.appendAssumeCapacity(@intFromEnum(case_vals[idx])); // item
  10723                         cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body
  10724                     }
  10725                 }
  10726                 const else_body_len = len: {
  10727                     // 'else' prong uses a bitcast
  10728                     var coerce_block = block.makeSubBlock();
  10729                     defer coerce_block.instructions.deinit(sema.gpa);
  10730 
  10731                     const first_imc_item_idx = in_mem_coercible.findFirstSet().?;
  10732                     const first_imc_field_idx = field_indices[first_imc_item_idx];
  10733                     const first_imc_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_imc_field_idx]);
  10734                     const uncoerced = try coerce_block.addStructFieldVal(operand_val, first_imc_field_idx, first_imc_field_ty);
  10735                     const coerced = try coerce_block.addBitCast(capture_ty, uncoerced);
  10736                     _ = try coerce_block.addBr(capture_block_inst, coerced);
  10737 
  10738                     try cases_extra.appendSlice(@ptrCast(coerce_block.instructions.items));
  10739                     break :len coerce_block.instructions.items.len;
  10740                 };
  10741 
  10742                 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
  10743                     cases_extra.items.len +
  10744                     @typeInfo(Air.Block).@"struct".fields.len +
  10745                     1);
  10746 
  10747                 const switch_br_inst: u32 = @intCast(sema.air_instructions.len);
  10748                 try sema.air_instructions.append(sema.gpa, .{
  10749                     .tag = .switch_br,
  10750                     .data = .{
  10751                         .pl_op = .{
  10752                             .operand = undefined, // set by switch below
  10753                             .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
  10754                                 .cases_len = @intCast(prong_count),
  10755                                 .else_body_len = @intCast(else_body_len),
  10756                             }),
  10757                         },
  10758                     },
  10759                 });
  10760                 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
  10761 
  10762                 // Set up block body
  10763                 switch (spa.operand) {
  10764                     .simple => |s| {
  10765                         const air_datas = sema.air_instructions.items(.data);
  10766                         air_datas[switch_br_inst].pl_op.operand = s.cond;
  10767                         air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
  10768                             .body_len = 1,
  10769                         });
  10770                         sema.air_extra.appendAssumeCapacity(switch_br_inst);
  10771                     },
  10772                     .loop => {
  10773                         // The block must first extract the tag from the loaded union.
  10774                         const tag_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  10775                         try sema.air_instructions.append(sema.gpa, .{
  10776                             .tag = .get_union_tag,
  10777                             .data = .{ .ty_op = .{
  10778                                 .ty = Air.internedToRef(union_obj.enum_tag_ty),
  10779                                 .operand = operand_val,
  10780                             } },
  10781                         });
  10782                         const air_datas = sema.air_instructions.items(.data);
  10783                         air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef();
  10784                         air_datas[@intFromEnum(capture_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{
  10785                             .body_len = 2,
  10786                         });
  10787                         sema.air_extra.appendAssumeCapacity(@intFromEnum(tag_inst));
  10788                         sema.air_extra.appendAssumeCapacity(switch_br_inst);
  10789                     },
  10790                 }
  10791 
  10792                 return capture_block_inst.toRef();
  10793             },
  10794             .error_set => {
  10795                 if (capture_byref) {
  10796                     return sema.fail(
  10797                         block,
  10798                         capture_src,
  10799                         "error set cannot be captured by reference",
  10800                         .{},
  10801                     );
  10802                 }
  10803 
  10804                 if (case_vals.len == 1) {
  10805                     const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, case_vals[0], undefined) catch unreachable;
  10806                     const item_ty = try pt.singleErrorSetType(item_val.getErrorName(zcu).unwrap().?);
  10807                     return sema.bitCast(block, item_ty, operand_val, operand_src, null);
  10808                 }
  10809 
  10810                 var names: InferredErrorSet.NameMap = .{};
  10811                 try names.ensureUnusedCapacity(sema.arena, case_vals.len);
  10812                 for (case_vals) |err| {
  10813                     const err_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, err, undefined) catch unreachable;
  10814                     names.putAssumeCapacityNoClobber(err_val.getErrorName(zcu).unwrap().?, {});
  10815                 }
  10816                 const error_ty = try pt.errorSetFromUnsortedNames(names.keys());
  10817                 return sema.bitCast(block, error_ty, operand_val, operand_src, null);
  10818             },
  10819             else => {
  10820                 // In this case the capture value is just the passed-through value
  10821                 // of the switch condition.
  10822                 if (capture_byref) {
  10823                     return operand_ptr;
  10824                 } else {
  10825                     return operand_val;
  10826                 }
  10827             },
  10828         }
  10829     }
  10830 };
  10831 
  10832 fn switchCond(
  10833     sema: *Sema,
  10834     block: *Block,
  10835     src: LazySrcLoc,
  10836     operand: Air.Inst.Ref,
  10837 ) CompileError!Air.Inst.Ref {
  10838     const pt = sema.pt;
  10839     const zcu = pt.zcu;
  10840     const operand_ty = sema.typeOf(operand);
  10841     switch (operand_ty.zigTypeTag(zcu)) {
  10842         .type,
  10843         .void,
  10844         .bool,
  10845         .int,
  10846         .float,
  10847         .comptime_float,
  10848         .comptime_int,
  10849         .enum_literal,
  10850         .pointer,
  10851         .@"fn",
  10852         .error_set,
  10853         .@"enum",
  10854         => {
  10855             if (operand_ty.isSlice(zcu)) {
  10856                 return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)});
  10857             }
  10858             if ((try sema.typeHasOnePossibleValue(operand_ty))) |opv| {
  10859                 return Air.internedToRef(opv.toIntern());
  10860             }
  10861             return operand;
  10862         },
  10863 
  10864         .@"union" => {
  10865             try operand_ty.resolveFields(pt);
  10866             const enum_ty = operand_ty.unionTagType(zcu) orelse {
  10867                 const msg = msg: {
  10868                     const msg = try sema.errMsg(src, "switch on union with no attached enum", .{});
  10869                     errdefer msg.destroy(sema.gpa);
  10870                     if (operand_ty.srcLocOrNull(zcu)) |union_src| {
  10871                         try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{});
  10872                     }
  10873                     break :msg msg;
  10874                 };
  10875                 return sema.failWithOwnedErrorMsg(block, msg);
  10876             };
  10877             return sema.unionToTag(block, enum_ty, operand, src);
  10878         },
  10879 
  10880         .error_union,
  10881         .noreturn,
  10882         .array,
  10883         .@"struct",
  10884         .undefined,
  10885         .null,
  10886         .optional,
  10887         .@"opaque",
  10888         .vector,
  10889         .frame,
  10890         .@"anyframe",
  10891         => return sema.fail(block, src, "switch on type '{f}'", .{operand_ty.fmt(pt)}),
  10892     }
  10893 }
  10894 
  10895 const SwitchErrorSet = std.AutoHashMap(InternPool.NullTerminatedString, LazySrcLoc);
  10896 
  10897 fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  10898     const tracy = trace(@src());
  10899     defer tracy.end();
  10900 
  10901     const pt = sema.pt;
  10902     const zcu = pt.zcu;
  10903     const gpa = sema.gpa;
  10904     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  10905     const switch_src = block.nodeOffset(inst_data.src_node);
  10906     const switch_src_node_offset = inst_data.src_node;
  10907     const switch_operand_src = block.src(.{ .node_offset_switch_operand = switch_src_node_offset });
  10908     const else_prong_src = block.src(.{ .node_offset_switch_special_prong = switch_src_node_offset });
  10909     const extra = sema.code.extraData(Zir.Inst.SwitchBlockErrUnion, inst_data.payload_index);
  10910     const main_operand_src = block.src(.{ .node_offset_if_cond = extra.data.main_src_node_offset });
  10911     const main_src = block.src(.{ .node_offset_main_token = extra.data.main_src_node_offset });
  10912 
  10913     const raw_operand_val = try sema.resolveInst(extra.data.operand);
  10914 
  10915     // AstGen guarantees that the instruction immediately preceding
  10916     // switch_block_err_union is a dbg_stmt
  10917     const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
  10918 
  10919     var header_extra_index: usize = extra.end;
  10920 
  10921     const scalar_cases_len = extra.data.bits.scalar_cases_len;
  10922     const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
  10923         const multi_cases_len = sema.code.extra[header_extra_index];
  10924         header_extra_index += 1;
  10925         break :blk multi_cases_len;
  10926     } else 0;
  10927 
  10928     const err_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_uses_err_capture) blk: {
  10929         const err_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
  10930         header_extra_index += 1;
  10931         // SwitchProngAnalysis wants inst_map to have space for the tag capture.
  10932         // Note that the normal capture is referred to via the switch block
  10933         // index, which there is already necessarily space for.
  10934         try sema.inst_map.ensureSpaceForInstructions(gpa, &.{err_capture_inst});
  10935         break :blk err_capture_inst;
  10936     } else undefined;
  10937 
  10938     var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
  10939     defer case_vals.deinit(gpa);
  10940 
  10941     const NonError = struct {
  10942         body: []const Zir.Inst.Index,
  10943         end: usize,
  10944         capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  10945     };
  10946 
  10947     const non_error_case: NonError = non_error: {
  10948         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
  10949         const extra_body_start = header_extra_index + 1;
  10950         break :non_error .{
  10951             .body = sema.code.bodySlice(extra_body_start, info.body_len),
  10952             .end = extra_body_start + info.body_len,
  10953             .capture = info.capture,
  10954         };
  10955     };
  10956 
  10957     const Else = struct {
  10958         body: []const Zir.Inst.Index,
  10959         end: usize,
  10960         is_inline: bool,
  10961         has_capture: bool,
  10962     };
  10963 
  10964     const else_case: Else = if (!extra.data.bits.has_else) .{
  10965         .body = &.{},
  10966         .end = non_error_case.end,
  10967         .is_inline = false,
  10968         .has_capture = false,
  10969     } else special: {
  10970         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[non_error_case.end]);
  10971         const extra_body_start = non_error_case.end + 1;
  10972         assert(info.capture != .by_ref);
  10973         assert(!info.has_tag_capture);
  10974         break :special .{
  10975             .body = sema.code.bodySlice(extra_body_start, info.body_len),
  10976             .end = extra_body_start + info.body_len,
  10977             .is_inline = info.is_inline,
  10978             .has_capture = info.capture != .none,
  10979         };
  10980     };
  10981 
  10982     var seen_errors = SwitchErrorSet.init(gpa);
  10983     defer seen_errors.deinit();
  10984 
  10985     const operand_ty = sema.typeOf(raw_operand_val);
  10986     const operand_err_set = if (extra.data.bits.payload_is_ref)
  10987         operand_ty.childType(zcu)
  10988     else
  10989         operand_ty;
  10990 
  10991     if (operand_err_set.zigTypeTag(zcu) != .error_union) {
  10992         return sema.fail(block, switch_src, "expected error union type, found '{f}'", .{
  10993             operand_ty.fmt(pt),
  10994         });
  10995     }
  10996 
  10997     const operand_err_set_ty = operand_err_set.errorUnionSet(zcu);
  10998 
  10999     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  11000     try sema.air_instructions.append(gpa, .{
  11001         .tag = .block,
  11002         .data = undefined,
  11003     });
  11004     var label: Block.Label = .{
  11005         .zir_block = inst,
  11006         .merges = .{
  11007             .src_locs = .{},
  11008             .results = .{},
  11009             .br_list = .{},
  11010             .block_inst = block_inst,
  11011         },
  11012     };
  11013 
  11014     var child_block: Block = .{
  11015         .parent = block,
  11016         .sema = sema,
  11017         .namespace = block.namespace,
  11018         .instructions = .{},
  11019         .label = &label,
  11020         .inlining = block.inlining,
  11021         .comptime_reason = block.comptime_reason,
  11022         .is_typeof = block.is_typeof,
  11023         .c_import_buf = block.c_import_buf,
  11024         .runtime_cond = block.runtime_cond,
  11025         .runtime_loop = block.runtime_loop,
  11026         .runtime_index = block.runtime_index,
  11027         .error_return_trace_index = block.error_return_trace_index,
  11028         .want_safety = block.want_safety,
  11029         .src_base_inst = block.src_base_inst,
  11030         .type_name_ctx = block.type_name_ctx,
  11031     };
  11032     const merges = &child_block.label.?.merges;
  11033     defer child_block.instructions.deinit(gpa);
  11034     defer merges.deinit(gpa);
  11035 
  11036     const resolved_err_set = try sema.resolveInferredErrorSetTy(block, main_src, operand_err_set_ty.toIntern());
  11037     if (Type.fromInterned(resolved_err_set).errorSetIsEmpty(zcu)) {
  11038         return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
  11039     }
  11040 
  11041     const else_error_ty: ?Type = try validateErrSetSwitch(
  11042         sema,
  11043         block,
  11044         &seen_errors,
  11045         &case_vals,
  11046         operand_err_set_ty,
  11047         inst_data,
  11048         scalar_cases_len,
  11049         multi_cases_len,
  11050         .{ .body = else_case.body, .end = else_case.end, .src = else_prong_src },
  11051         extra.data.bits.has_else,
  11052     );
  11053 
  11054     var spa: SwitchProngAnalysis = .{
  11055         .sema = sema,
  11056         .parent_block = block,
  11057         .operand = .{
  11058             .simple = .{
  11059                 .by_val = undefined, // must be set to the unwrapped error code before use
  11060                 .by_ref = undefined,
  11061                 .cond = raw_operand_val,
  11062             },
  11063         },
  11064         .else_error_ty = else_error_ty,
  11065         .switch_block_inst = inst,
  11066         .tag_capture_inst = undefined,
  11067     };
  11068 
  11069     if (try sema.resolveDefinedValue(&child_block, main_src, raw_operand_val)) |ov| {
  11070         const operand_val = if (extra.data.bits.payload_is_ref)
  11071             (try sema.pointerDeref(&child_block, main_src, ov, operand_ty)).?
  11072         else
  11073             ov;
  11074 
  11075         if (operand_val.errorUnionIsPayload(zcu)) {
  11076             return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
  11077         } else {
  11078             const err_val = Value.fromInterned(try pt.intern(.{
  11079                 .err = .{
  11080                     .ty = operand_err_set_ty.toIntern(),
  11081                     .name = operand_val.getErrorName(zcu).unwrap().?,
  11082                 },
  11083             }));
  11084             spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref)
  11085                 try sema.analyzeErrUnionCodePtr(block, switch_operand_src, raw_operand_val)
  11086             else
  11087                 try sema.analyzeErrUnionCode(block, switch_operand_src, raw_operand_val);
  11088 
  11089             if (extra.data.bits.any_uses_err_capture) {
  11090                 sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val);
  11091             }
  11092             defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
  11093 
  11094             return resolveSwitchComptime(
  11095                 sema,
  11096                 spa,
  11097                 &child_block,
  11098                 try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val),
  11099                 err_val,
  11100                 operand_err_set_ty,
  11101                 switch_src_node_offset,
  11102                 .{
  11103                     .body = else_case.body,
  11104                     .end = else_case.end,
  11105                     .capture = if (else_case.has_capture) .by_val else .none,
  11106                     .is_inline = else_case.is_inline,
  11107                     .has_tag_capture = false,
  11108                 },
  11109                 case_vals,
  11110                 scalar_cases_len,
  11111                 multi_cases_len,
  11112                 true,
  11113                 false,
  11114             );
  11115         }
  11116     }
  11117 
  11118     if (scalar_cases_len + multi_cases_len == 0) {
  11119         if (else_error_ty) |ty| if (ty.errorSetIsEmpty(zcu)) {
  11120             return sema.resolveBlockBody(block, main_operand_src, &child_block, non_error_case.body, inst, merges);
  11121         };
  11122     }
  11123 
  11124     if (child_block.isComptime()) {
  11125         _ = try sema.resolveConstDefinedValue(&child_block, main_operand_src, raw_operand_val, null);
  11126         unreachable;
  11127     }
  11128 
  11129     const cond = if (extra.data.bits.payload_is_ref) blk: {
  11130         try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val).elemType2(zcu));
  11131         const loaded = try sema.analyzeLoad(block, main_src, raw_operand_val, main_src);
  11132         break :blk try sema.analyzeIsNonErr(block, main_src, loaded);
  11133     } else blk: {
  11134         try sema.checkErrorType(block, main_src, sema.typeOf(raw_operand_val));
  11135         break :blk try sema.analyzeIsNonErr(block, main_src, raw_operand_val);
  11136     };
  11137 
  11138     var sub_block = child_block.makeSubBlock();
  11139     sub_block.runtime_loop = null;
  11140     sub_block.runtime_cond = main_operand_src;
  11141     sub_block.runtime_index.increment();
  11142     sub_block.need_debug_scope = null; // this body is emitted regardless
  11143     defer sub_block.instructions.deinit(gpa);
  11144 
  11145     const non_error_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, non_error_case.body);
  11146     const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
  11147     defer gpa.free(true_instructions);
  11148 
  11149     spa.operand.simple.by_val = if (extra.data.bits.payload_is_ref)
  11150         try sema.analyzeErrUnionCodePtr(&sub_block, switch_operand_src, raw_operand_val)
  11151     else
  11152         try sema.analyzeErrUnionCode(&sub_block, switch_operand_src, raw_operand_val);
  11153 
  11154     if (extra.data.bits.any_uses_err_capture) {
  11155         sema.inst_map.putAssumeCapacity(err_capture_inst, spa.operand.simple.by_val);
  11156     }
  11157     defer if (extra.data.bits.any_uses_err_capture) assert(sema.inst_map.remove(err_capture_inst));
  11158     _ = try sema.analyzeSwitchRuntimeBlock(
  11159         spa,
  11160         &sub_block,
  11161         switch_src,
  11162         try sema.switchCond(block, switch_operand_src, spa.operand.simple.by_val),
  11163         operand_err_set_ty,
  11164         switch_operand_src,
  11165         case_vals,
  11166         .{
  11167             .body = else_case.body,
  11168             .end = else_case.end,
  11169             .capture = if (else_case.has_capture) .by_val else .none,
  11170             .is_inline = else_case.is_inline,
  11171             .has_tag_capture = false,
  11172         },
  11173         scalar_cases_len,
  11174         multi_cases_len,
  11175         false,
  11176         undefined,
  11177         true,
  11178         switch_src_node_offset,
  11179         else_prong_src,
  11180         undefined,
  11181         seen_errors,
  11182         undefined,
  11183         undefined,
  11184         undefined,
  11185         cond_dbg_node_index,
  11186         true,
  11187     );
  11188 
  11189     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
  11190         true_instructions.len + sub_block.instructions.items.len);
  11191 
  11192     _ = try child_block.addInst(.{
  11193         .tag = .cond_br,
  11194         .data = .{
  11195             .pl_op = .{
  11196                 .operand = cond,
  11197                 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  11198                     .then_body_len = @intCast(true_instructions.len),
  11199                     .else_body_len = @intCast(sub_block.instructions.items.len),
  11200                     .branch_hints = .{
  11201                         .true = non_error_hint,
  11202                         .false = .none,
  11203                         // Code coverage is desired for error handling.
  11204                         .then_cov = .poi,
  11205                         .else_cov = .poi,
  11206                     },
  11207                 }),
  11208             },
  11209         },
  11210     });
  11211     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
  11212     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  11213 
  11214     return sema.resolveAnalyzedBlock(block, main_src, &child_block, merges, false);
  11215 }
  11216 
  11217 fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_ref: bool) CompileError!Air.Inst.Ref {
  11218     const tracy = trace(@src());
  11219     defer tracy.end();
  11220 
  11221     const pt = sema.pt;
  11222     const zcu = pt.zcu;
  11223     const gpa = sema.gpa;
  11224     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  11225     const src = block.nodeOffset(inst_data.src_node);
  11226     const src_node_offset = inst_data.src_node;
  11227     const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
  11228     const special_prong_src = block.src(.{ .node_offset_switch_special_prong = src_node_offset });
  11229     const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
  11230 
  11231     const operand: SwitchProngAnalysis.Operand, const raw_operand_ty: Type = op: {
  11232         const maybe_ptr = try sema.resolveInst(extra.data.operand);
  11233         const val, const ref = if (operand_is_ref)
  11234             .{ try sema.analyzeLoad(block, src, maybe_ptr, operand_src), maybe_ptr }
  11235         else
  11236             .{ maybe_ptr, undefined };
  11237 
  11238         const init_cond = try sema.switchCond(block, operand_src, val);
  11239 
  11240         const operand_ty = sema.typeOf(val);
  11241 
  11242         if (extra.data.bits.has_continue and !block.isComptime()) {
  11243             // Even if the operand is comptime-known, this `switch` is runtime.
  11244             if (try operand_ty.comptimeOnlySema(pt)) {
  11245                 return sema.failWithOwnedErrorMsg(block, msg: {
  11246                     const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)});
  11247                     errdefer msg.destroy(gpa);
  11248                     try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{});
  11249                     break :msg msg;
  11250                 });
  11251             }
  11252             try sema.validateRuntimeValue(block, operand_src, maybe_ptr);
  11253             const operand_alloc = if (extra.data.bits.any_non_inline_capture) a: {
  11254                 const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(maybe_ptr));
  11255                 const operand_alloc = try block.addTy(.alloc, operand_ptr_ty);
  11256                 _ = try block.addBinOp(.store, operand_alloc, maybe_ptr);
  11257                 break :a operand_alloc;
  11258             } else undefined;
  11259             break :op .{
  11260                 .{ .loop = .{
  11261                     .operand_alloc = operand_alloc,
  11262                     .operand_is_ref = operand_is_ref,
  11263                     .init_cond = init_cond,
  11264                 } },
  11265                 operand_ty,
  11266             };
  11267         }
  11268 
  11269         // We always use `simple` in the comptime case, because as far as the dispatching logic
  11270         // is concerned, it really is dispatching a single prong. `resolveSwitchComptime` will
  11271         // be resposible for recursively resolving different prongs as needed.
  11272         break :op .{
  11273             .{ .simple = .{
  11274                 .by_val = val,
  11275                 .by_ref = ref,
  11276                 .cond = init_cond,
  11277             } },
  11278             operand_ty,
  11279         };
  11280     };
  11281 
  11282     const union_originally = raw_operand_ty.zigTypeTag(zcu) == .@"union";
  11283     const err_set = raw_operand_ty.zigTypeTag(zcu) == .error_set;
  11284     const cond_ty = switch (raw_operand_ty.zigTypeTag(zcu)) {
  11285         .@"union" => raw_operand_ty.unionTagType(zcu).?, // validated by `switchCond` above
  11286         else => raw_operand_ty,
  11287     };
  11288 
  11289     // AstGen guarantees that the instruction immediately preceding
  11290     // switch_block(_ref) is a dbg_stmt
  11291     const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
  11292 
  11293     var header_extra_index: usize = extra.end;
  11294 
  11295     const scalar_cases_len = extra.data.bits.scalar_cases_len;
  11296     const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
  11297         const multi_cases_len = sema.code.extra[header_extra_index];
  11298         header_extra_index += 1;
  11299         break :blk multi_cases_len;
  11300     } else 0;
  11301 
  11302     const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
  11303         const tag_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
  11304         header_extra_index += 1;
  11305         // SwitchProngAnalysis wants inst_map to have space for the tag capture.
  11306         // Note that the normal capture is referred to via the switch block
  11307         // index, which there is already necessarily space for.
  11308         try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
  11309         break :blk tag_capture_inst;
  11310     } else undefined;
  11311 
  11312     var case_vals = try std.ArrayListUnmanaged(Air.Inst.Ref).initCapacity(gpa, scalar_cases_len + 2 * multi_cases_len);
  11313     defer case_vals.deinit(gpa);
  11314 
  11315     const special_prong = extra.data.bits.specialProng();
  11316     const special: SpecialProng = switch (special_prong) {
  11317         .none => .{
  11318             .body = &.{},
  11319             .end = header_extra_index,
  11320             .capture = .none,
  11321             .is_inline = false,
  11322             .has_tag_capture = false,
  11323         },
  11324         .under, .@"else" => blk: {
  11325             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
  11326             const extra_body_start = header_extra_index + 1;
  11327             break :blk .{
  11328                 .body = sema.code.bodySlice(extra_body_start, info.body_len),
  11329                 .end = extra_body_start + info.body_len,
  11330                 .capture = info.capture,
  11331                 .is_inline = info.is_inline,
  11332                 .has_tag_capture = info.has_tag_capture,
  11333             };
  11334         },
  11335     };
  11336 
  11337     // Duplicate checking variables later also used for `inline else`.
  11338     var seen_enum_fields: []?LazySrcLoc = &.{};
  11339     var seen_errors = SwitchErrorSet.init(gpa);
  11340     var range_set = RangeSet.init(gpa, zcu);
  11341     var true_count: u8 = 0;
  11342     var false_count: u8 = 0;
  11343 
  11344     defer {
  11345         range_set.deinit();
  11346         gpa.free(seen_enum_fields);
  11347         seen_errors.deinit();
  11348     }
  11349 
  11350     var empty_enum = false;
  11351 
  11352     var else_error_ty: ?Type = null;
  11353 
  11354     // Validate usage of '_' prongs.
  11355     if (special_prong == .under and !raw_operand_ty.isNonexhaustiveEnum(zcu)) {
  11356         const msg = msg: {
  11357             const msg = try sema.errMsg(
  11358                 src,
  11359                 "'_' prong only allowed when switching on non-exhaustive enums",
  11360                 .{},
  11361             );
  11362             errdefer msg.destroy(gpa);
  11363             try sema.errNote(
  11364                 special_prong_src,
  11365                 msg,
  11366                 "'_' prong here",
  11367                 .{},
  11368             );
  11369             try sema.errNote(
  11370                 src,
  11371                 msg,
  11372                 "consider using 'else'",
  11373                 .{},
  11374             );
  11375             break :msg msg;
  11376         };
  11377         return sema.failWithOwnedErrorMsg(block, msg);
  11378     }
  11379 
  11380     // Validate for duplicate items, missing else prong, and invalid range.
  11381     switch (cond_ty.zigTypeTag(zcu)) {
  11382         .@"union" => unreachable, // handled in `switchCond`
  11383         .@"enum" => {
  11384             seen_enum_fields = try gpa.alloc(?LazySrcLoc, cond_ty.enumFieldCount(zcu));
  11385             empty_enum = seen_enum_fields.len == 0 and !cond_ty.isNonexhaustiveEnum(zcu);
  11386             @memset(seen_enum_fields, null);
  11387             // `range_set` is used for non-exhaustive enum values that do not correspond to any tags.
  11388 
  11389             var extra_index: usize = special.end;
  11390             {
  11391                 var scalar_i: u32 = 0;
  11392                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11393                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11394                     extra_index += 1;
  11395                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11396                     extra_index += 1 + info.body_len;
  11397 
  11398                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
  11399                         block,
  11400                         seen_enum_fields,
  11401                         &range_set,
  11402                         item_ref,
  11403                         cond_ty,
  11404                         block.src(.{ .switch_case_item = .{
  11405                             .switch_node_offset = src_node_offset,
  11406                             .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  11407                             .item_idx = .{ .kind = .single, .index = 0 },
  11408                         } }),
  11409                     ));
  11410                 }
  11411             }
  11412             {
  11413                 var multi_i: u32 = 0;
  11414                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11415                     const items_len = sema.code.extra[extra_index];
  11416                     extra_index += 1;
  11417                     const ranges_len = sema.code.extra[extra_index];
  11418                     extra_index += 1;
  11419                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11420                     extra_index += 1;
  11421                     const items = sema.code.refSlice(extra_index, items_len);
  11422                     extra_index += items_len + info.body_len;
  11423 
  11424                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11425                     for (items, 0..) |item_ref, item_i| {
  11426                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemEnum(
  11427                             block,
  11428                             seen_enum_fields,
  11429                             &range_set,
  11430                             item_ref,
  11431                             cond_ty,
  11432                             block.src(.{ .switch_case_item = .{
  11433                                 .switch_node_offset = src_node_offset,
  11434                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11435                                 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  11436                             } }),
  11437                         ));
  11438                     }
  11439 
  11440                     try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
  11441                 }
  11442             }
  11443             const all_tags_handled = for (seen_enum_fields) |seen_src| {
  11444                 if (seen_src == null) break false;
  11445             } else true;
  11446 
  11447             if (special_prong == .@"else") {
  11448                 if (all_tags_handled and !cond_ty.isNonexhaustiveEnum(zcu)) return sema.fail(
  11449                     block,
  11450                     special_prong_src,
  11451                     "unreachable else prong; all cases already handled",
  11452                     .{},
  11453                 );
  11454             } else if (!all_tags_handled) {
  11455                 const msg = msg: {
  11456                     const msg = try sema.errMsg(
  11457                         src,
  11458                         "switch must handle all possibilities",
  11459                         .{},
  11460                     );
  11461                     errdefer msg.destroy(sema.gpa);
  11462                     for (seen_enum_fields, 0..) |seen_src, i| {
  11463                         if (seen_src != null) continue;
  11464 
  11465                         const field_name = cond_ty.enumFieldName(i, zcu);
  11466                         try sema.addFieldErrNote(
  11467                             cond_ty,
  11468                             i,
  11469                             msg,
  11470                             "unhandled enumeration value: '{f}'",
  11471                             .{field_name.fmt(&zcu.intern_pool)},
  11472                         );
  11473                     }
  11474                     try sema.errNote(
  11475                         cond_ty.srcLoc(zcu),
  11476                         msg,
  11477                         "enum '{f}' declared here",
  11478                         .{cond_ty.fmt(pt)},
  11479                     );
  11480                     break :msg msg;
  11481                 };
  11482                 return sema.failWithOwnedErrorMsg(block, msg);
  11483             } else if (special_prong == .none and cond_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
  11484                 return sema.fail(
  11485                     block,
  11486                     src,
  11487                     "switch on non-exhaustive enum must include 'else' or '_' prong",
  11488                     .{},
  11489                 );
  11490             }
  11491         },
  11492         .error_set => else_error_ty = try validateErrSetSwitch(
  11493             sema,
  11494             block,
  11495             &seen_errors,
  11496             &case_vals,
  11497             cond_ty,
  11498             inst_data,
  11499             scalar_cases_len,
  11500             multi_cases_len,
  11501             .{ .body = special.body, .end = special.end, .src = special_prong_src },
  11502             special_prong == .@"else",
  11503         ),
  11504         .int, .comptime_int => {
  11505             var extra_index: usize = special.end;
  11506             {
  11507                 var scalar_i: u32 = 0;
  11508                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11509                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11510                     extra_index += 1;
  11511                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11512                     extra_index += 1 + info.body_len;
  11513 
  11514                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
  11515                         block,
  11516                         &range_set,
  11517                         item_ref,
  11518                         cond_ty,
  11519                         block.src(.{ .switch_case_item = .{
  11520                             .switch_node_offset = src_node_offset,
  11521                             .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  11522                             .item_idx = .{ .kind = .single, .index = 0 },
  11523                         } }),
  11524                     ));
  11525                 }
  11526             }
  11527             {
  11528                 var multi_i: u32 = 0;
  11529                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11530                     const items_len = sema.code.extra[extra_index];
  11531                     extra_index += 1;
  11532                     const ranges_len = sema.code.extra[extra_index];
  11533                     extra_index += 1;
  11534                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11535                     extra_index += 1;
  11536                     const items = sema.code.refSlice(extra_index, items_len);
  11537                     extra_index += items_len;
  11538 
  11539                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11540                     for (items, 0..) |item_ref, item_i| {
  11541                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemInt(
  11542                             block,
  11543                             &range_set,
  11544                             item_ref,
  11545                             cond_ty,
  11546                             block.src(.{ .switch_case_item = .{
  11547                                 .switch_node_offset = src_node_offset,
  11548                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11549                                 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  11550                             } }),
  11551                         ));
  11552                     }
  11553 
  11554                     try case_vals.ensureUnusedCapacity(gpa, 2 * ranges_len);
  11555                     var range_i: u32 = 0;
  11556                     while (range_i < ranges_len) : (range_i += 1) {
  11557                         const item_first: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11558                         extra_index += 1;
  11559                         const item_last: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11560                         extra_index += 1;
  11561 
  11562                         const vals = try sema.validateSwitchRange(
  11563                             block,
  11564                             &range_set,
  11565                             item_first,
  11566                             item_last,
  11567                             cond_ty,
  11568                             block.src(.{ .switch_case_item = .{
  11569                                 .switch_node_offset = src_node_offset,
  11570                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11571                                 .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
  11572                             } }),
  11573                         );
  11574                         case_vals.appendAssumeCapacity(vals[0]);
  11575                         case_vals.appendAssumeCapacity(vals[1]);
  11576                     }
  11577 
  11578                     extra_index += info.body_len;
  11579                 }
  11580             }
  11581 
  11582             check_range: {
  11583                 if (cond_ty.zigTypeTag(zcu) == .int) {
  11584                     const min_int = try cond_ty.minInt(pt, cond_ty);
  11585                     const max_int = try cond_ty.maxInt(pt, cond_ty);
  11586                     if (try range_set.spans(min_int.toIntern(), max_int.toIntern())) {
  11587                         if (special_prong == .@"else") {
  11588                             return sema.fail(
  11589                                 block,
  11590                                 special_prong_src,
  11591                                 "unreachable else prong; all cases already handled",
  11592                                 .{},
  11593                             );
  11594                         }
  11595                         break :check_range;
  11596                     }
  11597                 }
  11598                 if (special_prong != .@"else") {
  11599                     return sema.fail(
  11600                         block,
  11601                         src,
  11602                         "switch must handle all possibilities",
  11603                         .{},
  11604                     );
  11605                 }
  11606             }
  11607         },
  11608         .bool => {
  11609             var extra_index: usize = special.end;
  11610             {
  11611                 var scalar_i: u32 = 0;
  11612                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11613                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11614                     extra_index += 1;
  11615                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11616                     extra_index += 1 + info.body_len;
  11617 
  11618                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
  11619                         block,
  11620                         &true_count,
  11621                         &false_count,
  11622                         item_ref,
  11623                         block.src(.{ .switch_case_item = .{
  11624                             .switch_node_offset = src_node_offset,
  11625                             .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  11626                             .item_idx = .{ .kind = .single, .index = 0 },
  11627                         } }),
  11628                     ));
  11629                 }
  11630             }
  11631             {
  11632                 var multi_i: u32 = 0;
  11633                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11634                     const items_len = sema.code.extra[extra_index];
  11635                     extra_index += 1;
  11636                     const ranges_len = sema.code.extra[extra_index];
  11637                     extra_index += 1;
  11638                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11639                     extra_index += 1;
  11640                     const items = sema.code.refSlice(extra_index, items_len);
  11641                     extra_index += items_len + info.body_len;
  11642 
  11643                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11644                     for (items, 0..) |item_ref, item_i| {
  11645                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemBool(
  11646                             block,
  11647                             &true_count,
  11648                             &false_count,
  11649                             item_ref,
  11650                             block.src(.{ .switch_case_item = .{
  11651                                 .switch_node_offset = src_node_offset,
  11652                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11653                                 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  11654                             } }),
  11655                         ));
  11656                     }
  11657 
  11658                     try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
  11659                 }
  11660             }
  11661             switch (special_prong) {
  11662                 .@"else" => {
  11663                     if (true_count + false_count == 2) {
  11664                         return sema.fail(
  11665                             block,
  11666                             special_prong_src,
  11667                             "unreachable else prong; all cases already handled",
  11668                             .{},
  11669                         );
  11670                     }
  11671                 },
  11672                 .under, .none => {
  11673                     if (true_count + false_count < 2) {
  11674                         return sema.fail(
  11675                             block,
  11676                             src,
  11677                             "switch must handle all possibilities",
  11678                             .{},
  11679                         );
  11680                     }
  11681                 },
  11682             }
  11683         },
  11684         .enum_literal, .void, .@"fn", .pointer, .type => {
  11685             if (special_prong != .@"else") {
  11686                 return sema.fail(
  11687                     block,
  11688                     src,
  11689                     "else prong required when switching on type '{f}'",
  11690                     .{cond_ty.fmt(pt)},
  11691                 );
  11692             }
  11693 
  11694             var seen_values = ValueSrcMap{};
  11695             defer seen_values.deinit(gpa);
  11696 
  11697             var extra_index: usize = special.end;
  11698             {
  11699                 var scalar_i: u32 = 0;
  11700                 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  11701                     const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  11702                     extra_index += 1;
  11703                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11704                     extra_index += 1;
  11705                     extra_index += info.body_len;
  11706 
  11707                     case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
  11708                         block,
  11709                         &seen_values,
  11710                         item_ref,
  11711                         cond_ty,
  11712                         block.src(.{ .switch_case_item = .{
  11713                             .switch_node_offset = src_node_offset,
  11714                             .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  11715                             .item_idx = .{ .kind = .single, .index = 0 },
  11716                         } }),
  11717                     ));
  11718                 }
  11719             }
  11720             {
  11721                 var multi_i: u32 = 0;
  11722                 while (multi_i < multi_cases_len) : (multi_i += 1) {
  11723                     const items_len = sema.code.extra[extra_index];
  11724                     extra_index += 1;
  11725                     const ranges_len = sema.code.extra[extra_index];
  11726                     extra_index += 1;
  11727                     const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  11728                     extra_index += 1;
  11729                     const items = sema.code.refSlice(extra_index, items_len);
  11730                     extra_index += items_len + info.body_len;
  11731 
  11732                     try case_vals.ensureUnusedCapacity(gpa, items.len);
  11733                     for (items, 0..) |item_ref, item_i| {
  11734                         case_vals.appendAssumeCapacity(try sema.validateSwitchItemSparse(
  11735                             block,
  11736                             &seen_values,
  11737                             item_ref,
  11738                             cond_ty,
  11739                             block.src(.{ .switch_case_item = .{
  11740                                 .switch_node_offset = src_node_offset,
  11741                                 .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  11742                                 .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  11743                             } }),
  11744                         ));
  11745                     }
  11746 
  11747                     try sema.validateSwitchNoRange(block, ranges_len, cond_ty, src_node_offset);
  11748                 }
  11749             }
  11750         },
  11751 
  11752         .error_union,
  11753         .noreturn,
  11754         .array,
  11755         .@"struct",
  11756         .undefined,
  11757         .null,
  11758         .optional,
  11759         .@"opaque",
  11760         .vector,
  11761         .frame,
  11762         .@"anyframe",
  11763         .comptime_float,
  11764         .float,
  11765         => return sema.fail(block, operand_src, "invalid switch operand type '{f}'", .{
  11766             raw_operand_ty.fmt(pt),
  11767         }),
  11768     }
  11769 
  11770     const spa: SwitchProngAnalysis = .{
  11771         .sema = sema,
  11772         .parent_block = block,
  11773         .operand = operand,
  11774         .else_error_ty = else_error_ty,
  11775         .switch_block_inst = inst,
  11776         .tag_capture_inst = tag_capture_inst,
  11777     };
  11778 
  11779     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  11780     try sema.air_instructions.append(gpa, .{
  11781         .tag = .block,
  11782         .data = undefined,
  11783     });
  11784     var label: Block.Label = .{
  11785         .zir_block = inst,
  11786         .merges = .{
  11787             .src_locs = .{},
  11788             .results = .{},
  11789             .br_list = .{},
  11790             .block_inst = block_inst,
  11791         },
  11792     };
  11793 
  11794     var child_block: Block = .{
  11795         .parent = block,
  11796         .sema = sema,
  11797         .namespace = block.namespace,
  11798         .instructions = .{},
  11799         .label = &label,
  11800         .inlining = block.inlining,
  11801         .comptime_reason = block.comptime_reason,
  11802         .is_typeof = block.is_typeof,
  11803         .c_import_buf = block.c_import_buf,
  11804         .runtime_cond = block.runtime_cond,
  11805         .runtime_loop = block.runtime_loop,
  11806         .runtime_index = block.runtime_index,
  11807         .want_safety = block.want_safety,
  11808         .error_return_trace_index = block.error_return_trace_index,
  11809         .src_base_inst = block.src_base_inst,
  11810         .type_name_ctx = block.type_name_ctx,
  11811     };
  11812     const merges = &child_block.label.?.merges;
  11813     defer child_block.instructions.deinit(gpa);
  11814     defer merges.deinit(gpa);
  11815 
  11816     if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline) {
  11817         if (empty_enum) {
  11818             return .void_value;
  11819         }
  11820         if (special_prong == .none) {
  11821             return sema.fail(block, src, "switch must handle all possibilities", .{});
  11822         }
  11823         const init_cond = switch (operand) {
  11824             .simple => |s| s.cond,
  11825             .loop => |l| l.init_cond,
  11826         };
  11827         if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
  11828             raw_operand_ty.zigTypeTag(zcu) == .@"enum" and !raw_operand_ty.isNonexhaustiveEnum(zcu))
  11829         {
  11830             try sema.zirDbgStmt(block, cond_dbg_node_index);
  11831             const ok = try block.addUnOp(.is_named_enum_value, init_cond);
  11832             try sema.addSafetyCheck(block, src, ok, .corrupt_switch);
  11833         }
  11834         if (err_set and try sema.maybeErrorUnwrap(block, special.body, init_cond, operand_src, false)) {
  11835             return .unreachable_value;
  11836         }
  11837     }
  11838 
  11839     switch (operand) {
  11840         .loop => {}, // always runtime; evaluation in comptime scope uses `simple`
  11841         .simple => |s| {
  11842             if (try sema.resolveDefinedValue(&child_block, src, s.cond)) |cond_val| {
  11843                 return resolveSwitchComptimeLoop(
  11844                     sema,
  11845                     spa,
  11846                     &child_block,
  11847                     if (operand_is_ref)
  11848                         sema.typeOf(s.by_ref)
  11849                     else
  11850                         raw_operand_ty,
  11851                     cond_ty,
  11852                     cond_val,
  11853                     src_node_offset,
  11854                     special,
  11855                     case_vals,
  11856                     scalar_cases_len,
  11857                     multi_cases_len,
  11858                     err_set,
  11859                     empty_enum,
  11860                     operand_is_ref,
  11861                 );
  11862             }
  11863 
  11864             if (scalar_cases_len + multi_cases_len == 0 and !special.is_inline and !extra.data.bits.has_continue) {
  11865                 return spa.resolveProngComptime(
  11866                     &child_block,
  11867                     .special,
  11868                     special.body,
  11869                     special.capture,
  11870                     block.src(.{ .switch_capture = .{
  11871                         .switch_node_offset = src_node_offset,
  11872                         .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
  11873                     } }),
  11874                     undefined, // case_vals may be undefined for special prongs
  11875                     .none,
  11876                     false,
  11877                     merges,
  11878                 );
  11879             }
  11880         },
  11881     }
  11882 
  11883     if (child_block.isComptime()) {
  11884         _ = try sema.resolveConstDefinedValue(&child_block, operand_src, operand.simple.cond, null);
  11885         unreachable;
  11886     }
  11887 
  11888     const air_switch_ref = try sema.analyzeSwitchRuntimeBlock(
  11889         spa,
  11890         &child_block,
  11891         src,
  11892         switch (operand) {
  11893             .simple => |s| s.cond,
  11894             .loop => |l| l.init_cond,
  11895         },
  11896         cond_ty,
  11897         operand_src,
  11898         case_vals,
  11899         special,
  11900         scalar_cases_len,
  11901         multi_cases_len,
  11902         union_originally,
  11903         raw_operand_ty,
  11904         err_set,
  11905         src_node_offset,
  11906         special_prong_src,
  11907         seen_enum_fields,
  11908         seen_errors,
  11909         range_set,
  11910         true_count,
  11911         false_count,
  11912         cond_dbg_node_index,
  11913         false,
  11914     );
  11915 
  11916     for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
  11917         var replacement_block = block.makeSubBlock();
  11918         defer replacement_block.instructions.deinit(gpa);
  11919 
  11920         assert(sema.air_instructions.items(.tag)[@intFromEnum(placeholder_inst)] == .br);
  11921         const new_operand_maybe_ref = sema.air_instructions.items(.data)[@intFromEnum(placeholder_inst)].br.operand;
  11922 
  11923         if (extra.data.bits.any_non_inline_capture) {
  11924             _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref);
  11925         }
  11926 
  11927         const new_operand_val = if (operand_is_ref)
  11928             try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src)
  11929         else
  11930             new_operand_maybe_ref;
  11931 
  11932         const new_cond = try sema.switchCond(&replacement_block, dispatch_src, new_operand_val);
  11933 
  11934         if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
  11935             cond_ty.zigTypeTag(zcu) == .@"enum" and !cond_ty.isNonexhaustiveEnum(zcu) and
  11936             !try sema.isComptimeKnown(new_cond))
  11937         {
  11938             const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond);
  11939             try sema.addSafetyCheck(&replacement_block, src, ok, .corrupt_switch);
  11940         }
  11941 
  11942         _ = try replacement_block.addInst(.{
  11943             .tag = .switch_dispatch,
  11944             .data = .{ .br = .{
  11945                 .block_inst = air_switch_ref.toIndex().?,
  11946                 .operand = new_cond,
  11947             } },
  11948         });
  11949 
  11950         if (replacement_block.instructions.items.len == 1) {
  11951             // Optimization: we don't need a block!
  11952             sema.air_instructions.set(
  11953                 @intFromEnum(placeholder_inst),
  11954                 sema.air_instructions.get(@intFromEnum(replacement_block.instructions.items[0])),
  11955             );
  11956             continue;
  11957         }
  11958 
  11959         // Replace placeholder with a block.
  11960         // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`.
  11961         try sema.air_extra.ensureUnusedCapacity(
  11962             gpa,
  11963             @typeInfo(Air.Block).@"struct".fields.len + replacement_block.instructions.items.len,
  11964         );
  11965         sema.air_instructions.set(@intFromEnum(placeholder_inst), .{
  11966             .tag = .block,
  11967             .data = .{ .ty_pl = .{
  11968                 .ty = .noreturn_type,
  11969                 .payload = sema.addExtraAssumeCapacity(Air.Block{
  11970                     .body_len = @intCast(replacement_block.instructions.items.len),
  11971                 }),
  11972             } },
  11973         });
  11974         sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
  11975     }
  11976 
  11977     return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
  11978 }
  11979 
  11980 const SpecialProng = struct {
  11981     body: []const Zir.Inst.Index,
  11982     end: usize,
  11983     capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
  11984     is_inline: bool,
  11985     has_tag_capture: bool,
  11986 };
  11987 
  11988 fn analyzeSwitchRuntimeBlock(
  11989     sema: *Sema,
  11990     spa: SwitchProngAnalysis,
  11991     child_block: *Block,
  11992     src: LazySrcLoc,
  11993     operand: Air.Inst.Ref,
  11994     operand_ty: Type,
  11995     operand_src: LazySrcLoc,
  11996     case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
  11997     special: SpecialProng,
  11998     scalar_cases_len: usize,
  11999     multi_cases_len: usize,
  12000     union_originally: bool,
  12001     maybe_union_ty: Type,
  12002     err_set: bool,
  12003     switch_node_offset: std.zig.Ast.Node.Offset,
  12004     special_prong_src: LazySrcLoc,
  12005     seen_enum_fields: []?LazySrcLoc,
  12006     seen_errors: SwitchErrorSet,
  12007     range_set: RangeSet,
  12008     true_count: u8,
  12009     false_count: u8,
  12010     cond_dbg_node_index: Zir.Inst.Index,
  12011     allow_err_code_unwrap: bool,
  12012 ) CompileError!Air.Inst.Ref {
  12013     const pt = sema.pt;
  12014     const zcu = pt.zcu;
  12015     const gpa = sema.gpa;
  12016     const ip = &zcu.intern_pool;
  12017 
  12018     const block = child_block.parent.?;
  12019 
  12020     const estimated_cases_extra = (scalar_cases_len + multi_cases_len) *
  12021         @typeInfo(Air.SwitchBr.Case).@"struct".fields.len + 2;
  12022     var cases_extra = try std.ArrayListUnmanaged(u32).initCapacity(gpa, estimated_cases_extra);
  12023     defer cases_extra.deinit(gpa);
  12024 
  12025     var branch_hints = try std.ArrayListUnmanaged(std.builtin.BranchHint).initCapacity(gpa, scalar_cases_len);
  12026     defer branch_hints.deinit(gpa);
  12027 
  12028     var case_block = child_block.makeSubBlock();
  12029     case_block.runtime_loop = null;
  12030     case_block.runtime_cond = operand_src;
  12031     case_block.runtime_index.increment();
  12032     case_block.need_debug_scope = null; // this body is emitted regardless
  12033     defer case_block.instructions.deinit(gpa);
  12034 
  12035     var extra_index: usize = special.end;
  12036 
  12037     var scalar_i: usize = 0;
  12038     while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  12039         extra_index += 1;
  12040         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  12041         extra_index += 1;
  12042         const body = sema.code.bodySlice(extra_index, info.body_len);
  12043         extra_index += info.body_len;
  12044 
  12045         case_block.instructions.shrinkRetainingCapacity(0);
  12046         case_block.error_return_trace_index = child_block.error_return_trace_index;
  12047 
  12048         const item = case_vals.items[scalar_i];
  12049         // `item` is already guaranteed to be constant known.
  12050 
  12051         const analyze_body = if (union_originally) blk: {
  12052             const unresolved_item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  12053             const item_val = sema.resolveLazyValue(unresolved_item_val) catch unreachable;
  12054             const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
  12055             break :blk field_ty.zigTypeTag(zcu) != .noreturn;
  12056         } else true;
  12057 
  12058         const prong_hint: std.builtin.BranchHint = if (err_set and
  12059             try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
  12060         h: {
  12061             // nothing to do here. weight against error branch
  12062             break :h .unlikely;
  12063         } else if (analyze_body) h: {
  12064             break :h try spa.analyzeProngRuntime(
  12065                 &case_block,
  12066                 .normal,
  12067                 body,
  12068                 info.capture,
  12069                 child_block.src(.{ .switch_capture = .{
  12070                     .switch_node_offset = switch_node_offset,
  12071                     .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  12072                 } }),
  12073                 &.{item},
  12074                 if (info.is_inline) item else .none,
  12075                 info.has_tag_capture,
  12076             );
  12077         } else h: {
  12078             _ = try case_block.addNoOp(.unreach);
  12079             break :h .none;
  12080         };
  12081 
  12082         try branch_hints.append(gpa, prong_hint);
  12083         try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12084             1 + // `item`, no ranges
  12085             case_block.instructions.items.len);
  12086         cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12087             .items_len = 1,
  12088             .ranges_len = 0,
  12089             .body_len = @intCast(case_block.instructions.items.len),
  12090         }));
  12091         cases_extra.appendAssumeCapacity(@intFromEnum(item));
  12092         cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12093     }
  12094 
  12095     var cases_len = scalar_cases_len;
  12096     var case_val_idx: usize = scalar_cases_len;
  12097     var multi_i: u32 = 0;
  12098     while (multi_i < multi_cases_len) : (multi_i += 1) {
  12099         const items_len = sema.code.extra[extra_index];
  12100         extra_index += 1;
  12101         const ranges_len = sema.code.extra[extra_index];
  12102         extra_index += 1;
  12103         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  12104         extra_index += 1 + items_len + 2 * ranges_len;
  12105 
  12106         const items = case_vals.items[case_val_idx..][0..items_len];
  12107         case_val_idx += items_len;
  12108         // TODO: @ptrCast slice once Sema supports it
  12109         const ranges: []const [2]Air.Inst.Ref = @as([*]const [2]Air.Inst.Ref, @ptrCast(case_vals.items[case_val_idx..]))[0..ranges_len];
  12110         case_val_idx += ranges_len * 2;
  12111 
  12112         const body = sema.code.bodySlice(extra_index, info.body_len);
  12113         extra_index += info.body_len;
  12114 
  12115         case_block.instructions.shrinkRetainingCapacity(0);
  12116         case_block.error_return_trace_index = child_block.error_return_trace_index;
  12117 
  12118         // Generate all possible cases as scalar prongs.
  12119         if (info.is_inline) {
  12120             var emit_bb = false;
  12121 
  12122             for (ranges, 0..) |range_items, range_i| {
  12123                 var item = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable;
  12124                 const item_last = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable;
  12125 
  12126                 while (item.compareScalar(.lte, item_last, operand_ty, zcu)) : ({
  12127                     // Previous validation has resolved any possible lazy values.
  12128                     const result = try arith.incrementDefinedInt(sema, operand_ty, item);
  12129                     assert(!result.overflow);
  12130                     item = result.val;
  12131                 }) {
  12132                     cases_len += 1;
  12133 
  12134                     const item_ref = Air.internedToRef(item.toIntern());
  12135 
  12136                     case_block.instructions.shrinkRetainingCapacity(0);
  12137                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12138 
  12139                     if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{
  12140                         .switch_node_offset = switch_node_offset,
  12141                         .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12142                         .item_idx = .{ .kind = .range, .index = @intCast(range_i) },
  12143                     } }));
  12144                     emit_bb = true;
  12145 
  12146                     const prong_hint = try spa.analyzeProngRuntime(
  12147                         &case_block,
  12148                         .normal,
  12149                         body,
  12150                         info.capture,
  12151                         child_block.src(.{ .switch_capture = .{
  12152                             .switch_node_offset = switch_node_offset,
  12153                             .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12154                         } }),
  12155                         undefined, // case_vals may be undefined for ranges
  12156                         item_ref,
  12157                         info.has_tag_capture,
  12158                     );
  12159                     try branch_hints.append(gpa, prong_hint);
  12160 
  12161                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12162                         1 + // `item`, no ranges
  12163                         case_block.instructions.items.len);
  12164                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12165                         .items_len = 1,
  12166                         .ranges_len = 0,
  12167                         .body_len = @intCast(case_block.instructions.items.len),
  12168                     }));
  12169                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12170                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12171 
  12172                     if (item.compareScalar(.eq, item_last, operand_ty, zcu)) break;
  12173                 }
  12174             }
  12175 
  12176             for (items, 0..) |item, item_i| {
  12177                 cases_len += 1;
  12178 
  12179                 case_block.instructions.shrinkRetainingCapacity(0);
  12180                 case_block.error_return_trace_index = child_block.error_return_trace_index;
  12181 
  12182                 const analyze_body = if (union_originally) blk: {
  12183                     const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  12184                     const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
  12185                     break :blk field_ty.zigTypeTag(zcu) != .noreturn;
  12186                 } else true;
  12187 
  12188                 if (emit_bb) try sema.emitBackwardBranch(block, block.src(.{ .switch_case_item = .{
  12189                     .switch_node_offset = switch_node_offset,
  12190                     .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12191                     .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  12192                 } }));
  12193                 emit_bb = true;
  12194 
  12195                 const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
  12196                     break :h try spa.analyzeProngRuntime(
  12197                         &case_block,
  12198                         .normal,
  12199                         body,
  12200                         info.capture,
  12201                         child_block.src(.{ .switch_capture = .{
  12202                             .switch_node_offset = switch_node_offset,
  12203                             .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12204                         } }),
  12205                         &.{item},
  12206                         item,
  12207                         info.has_tag_capture,
  12208                     );
  12209                 } else h: {
  12210                     _ = try case_block.addNoOp(.unreach);
  12211                     break :h .none;
  12212                 };
  12213                 try branch_hints.append(gpa, prong_hint);
  12214 
  12215                 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12216                     1 + // `item`, no ranges
  12217                     case_block.instructions.items.len);
  12218                 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12219                     .items_len = 1,
  12220                     .ranges_len = 0,
  12221                     .body_len = @intCast(case_block.instructions.items.len),
  12222                 }));
  12223                 cases_extra.appendAssumeCapacity(@intFromEnum(item));
  12224                 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12225             }
  12226 
  12227             continue;
  12228         }
  12229 
  12230         cases_len += 1;
  12231 
  12232         const analyze_body = if (union_originally)
  12233             for (items) |item| {
  12234                 const item_val = sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  12235                 const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
  12236                 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
  12237             } else false
  12238         else
  12239             true;
  12240 
  12241         const prong_hint: std.builtin.BranchHint = if (err_set and
  12242             try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src, allow_err_code_unwrap))
  12243         h: {
  12244             // nothing to do here. weight against error branch
  12245             break :h .unlikely;
  12246         } else if (analyze_body) h: {
  12247             break :h try spa.analyzeProngRuntime(
  12248                 &case_block,
  12249                 .normal,
  12250                 body,
  12251                 info.capture,
  12252                 child_block.src(.{ .switch_capture = .{
  12253                     .switch_node_offset = switch_node_offset,
  12254                     .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12255                 } }),
  12256                 items,
  12257                 .none,
  12258                 false,
  12259             );
  12260         } else h: {
  12261             _ = try case_block.addNoOp(.unreach);
  12262             break :h .none;
  12263         };
  12264 
  12265         try branch_hints.append(gpa, prong_hint);
  12266 
  12267         try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12268             items.len + 2 * ranges_len +
  12269             case_block.instructions.items.len);
  12270         cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12271             .items_len = @intCast(items.len),
  12272             .ranges_len = @intCast(ranges_len),
  12273             .body_len = @intCast(case_block.instructions.items.len),
  12274         }));
  12275 
  12276         for (items) |item| {
  12277             cases_extra.appendAssumeCapacity(@intFromEnum(item));
  12278         }
  12279         for (ranges) |range| {
  12280             cases_extra.appendSliceAssumeCapacity(&.{
  12281                 @intFromEnum(range[0]),
  12282                 @intFromEnum(range[1]),
  12283             });
  12284         }
  12285 
  12286         cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12287     }
  12288 
  12289     const else_body: []const Air.Inst.Index = if (special.body.len != 0 or case_block.wantSafety()) else_body: {
  12290         var emit_bb = false;
  12291         if (special.is_inline) switch (operand_ty.zigTypeTag(zcu)) {
  12292             .@"enum" => {
  12293                 if (operand_ty.isNonexhaustiveEnum(zcu) and !union_originally) {
  12294                     return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
  12295                         operand_ty.fmt(pt),
  12296                     });
  12297                 }
  12298                 for (seen_enum_fields, 0..) |f, i| {
  12299                     if (f != null) continue;
  12300                     cases_len += 1;
  12301 
  12302                     const item_val = try pt.enumValueFieldIndex(operand_ty, @intCast(i));
  12303                     const item_ref = Air.internedToRef(item_val.toIntern());
  12304 
  12305                     case_block.instructions.shrinkRetainingCapacity(0);
  12306                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12307 
  12308                     const analyze_body = if (union_originally) blk: {
  12309                         const field_ty = maybe_union_ty.unionFieldType(item_val, zcu).?;
  12310                         break :blk field_ty.zigTypeTag(zcu) != .noreturn;
  12311                     } else true;
  12312 
  12313                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12314                     emit_bb = true;
  12315 
  12316                     const prong_hint: std.builtin.BranchHint = if (analyze_body) h: {
  12317                         break :h try spa.analyzeProngRuntime(
  12318                             &case_block,
  12319                             .special,
  12320                             special.body,
  12321                             special.capture,
  12322                             child_block.src(.{ .switch_capture = .{
  12323                                 .switch_node_offset = switch_node_offset,
  12324                                 .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
  12325                             } }),
  12326                             &.{item_ref},
  12327                             item_ref,
  12328                             special.has_tag_capture,
  12329                         );
  12330                     } else h: {
  12331                         _ = try case_block.addNoOp(.unreach);
  12332                         break :h .none;
  12333                     };
  12334                     try branch_hints.append(gpa, prong_hint);
  12335 
  12336                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12337                         1 + // `item`, no ranges
  12338                         case_block.instructions.items.len);
  12339                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12340                         .items_len = 1,
  12341                         .ranges_len = 0,
  12342                         .body_len = @intCast(case_block.instructions.items.len),
  12343                     }));
  12344                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12345                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12346                 }
  12347             },
  12348             .error_set => {
  12349                 if (operand_ty.isAnyError(zcu)) {
  12350                     return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
  12351                         operand_ty.fmt(pt),
  12352                     });
  12353                 }
  12354                 const error_names = operand_ty.errorSetNames(zcu);
  12355                 for (0..error_names.len) |name_index| {
  12356                     const error_name = error_names.get(ip)[name_index];
  12357                     if (seen_errors.contains(error_name)) continue;
  12358                     cases_len += 1;
  12359 
  12360                     const item_val = try pt.intern(.{ .err = .{
  12361                         .ty = operand_ty.toIntern(),
  12362                         .name = error_name,
  12363                     } });
  12364                     const item_ref = Air.internedToRef(item_val);
  12365 
  12366                     case_block.instructions.shrinkRetainingCapacity(0);
  12367                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12368 
  12369                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12370                     emit_bb = true;
  12371 
  12372                     const prong_hint = try spa.analyzeProngRuntime(
  12373                         &case_block,
  12374                         .special,
  12375                         special.body,
  12376                         special.capture,
  12377                         child_block.src(.{ .switch_capture = .{
  12378                             .switch_node_offset = switch_node_offset,
  12379                             .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
  12380                         } }),
  12381                         &.{item_ref},
  12382                         item_ref,
  12383                         special.has_tag_capture,
  12384                     );
  12385                     try branch_hints.append(gpa, prong_hint);
  12386 
  12387                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12388                         1 + // `item`, no ranges
  12389                         case_block.instructions.items.len);
  12390                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12391                         .items_len = 1,
  12392                         .ranges_len = 0,
  12393                         .body_len = @intCast(case_block.instructions.items.len),
  12394                     }));
  12395                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12396                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12397                 }
  12398             },
  12399             .int => {
  12400                 var it = try RangeSetUnhandledIterator.init(sema, operand_ty, range_set);
  12401                 while (try it.next()) |cur| {
  12402                     cases_len += 1;
  12403 
  12404                     const item_ref = Air.internedToRef(cur);
  12405 
  12406                     case_block.instructions.shrinkRetainingCapacity(0);
  12407                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12408 
  12409                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12410                     emit_bb = true;
  12411 
  12412                     const prong_hint = try spa.analyzeProngRuntime(
  12413                         &case_block,
  12414                         .special,
  12415                         special.body,
  12416                         special.capture,
  12417                         child_block.src(.{ .switch_capture = .{
  12418                             .switch_node_offset = switch_node_offset,
  12419                             .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
  12420                         } }),
  12421                         &.{item_ref},
  12422                         item_ref,
  12423                         special.has_tag_capture,
  12424                     );
  12425                     try branch_hints.append(gpa, prong_hint);
  12426 
  12427                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12428                         1 + // `item`, no ranges
  12429                         case_block.instructions.items.len);
  12430                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12431                         .items_len = 1,
  12432                         .ranges_len = 0,
  12433                         .body_len = @intCast(case_block.instructions.items.len),
  12434                     }));
  12435                     cases_extra.appendAssumeCapacity(@intFromEnum(item_ref));
  12436                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12437                 }
  12438             },
  12439             .bool => {
  12440                 if (true_count == 0) {
  12441                     cases_len += 1;
  12442 
  12443                     case_block.instructions.shrinkRetainingCapacity(0);
  12444                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12445 
  12446                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12447                     emit_bb = true;
  12448 
  12449                     const prong_hint = try spa.analyzeProngRuntime(
  12450                         &case_block,
  12451                         .special,
  12452                         special.body,
  12453                         special.capture,
  12454                         child_block.src(.{ .switch_capture = .{
  12455                             .switch_node_offset = switch_node_offset,
  12456                             .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
  12457                         } }),
  12458                         &.{.bool_true},
  12459                         .bool_true,
  12460                         special.has_tag_capture,
  12461                     );
  12462                     try branch_hints.append(gpa, prong_hint);
  12463 
  12464                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12465                         1 + // `item`, no ranges
  12466                         case_block.instructions.items.len);
  12467                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12468                         .items_len = 1,
  12469                         .ranges_len = 0,
  12470                         .body_len = @intCast(case_block.instructions.items.len),
  12471                     }));
  12472                     cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_true));
  12473                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12474                 }
  12475                 if (false_count == 0) {
  12476                     cases_len += 1;
  12477 
  12478                     case_block.instructions.shrinkRetainingCapacity(0);
  12479                     case_block.error_return_trace_index = child_block.error_return_trace_index;
  12480 
  12481                     if (emit_bb) try sema.emitBackwardBranch(block, special_prong_src);
  12482                     emit_bb = true;
  12483 
  12484                     const prong_hint = try spa.analyzeProngRuntime(
  12485                         &case_block,
  12486                         .special,
  12487                         special.body,
  12488                         special.capture,
  12489                         child_block.src(.{ .switch_capture = .{
  12490                             .switch_node_offset = switch_node_offset,
  12491                             .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
  12492                         } }),
  12493                         &.{.bool_false},
  12494                         .bool_false,
  12495                         special.has_tag_capture,
  12496                     );
  12497                     try branch_hints.append(gpa, prong_hint);
  12498 
  12499                     try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".fields.len +
  12500                         1 + // `item`, no ranges
  12501                         case_block.instructions.items.len);
  12502                     cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
  12503                         .items_len = 1,
  12504                         .ranges_len = 0,
  12505                         .body_len = @intCast(case_block.instructions.items.len),
  12506                     }));
  12507                     cases_extra.appendAssumeCapacity(@intFromEnum(Air.Inst.Ref.bool_false));
  12508                     cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
  12509                 }
  12510             },
  12511             else => return sema.fail(block, special_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
  12512                 operand_ty.fmt(pt),
  12513             }),
  12514         };
  12515 
  12516         case_block.instructions.shrinkRetainingCapacity(0);
  12517         case_block.error_return_trace_index = child_block.error_return_trace_index;
  12518 
  12519         if (zcu.backendSupportsFeature(.is_named_enum_value) and
  12520             special.body.len != 0 and block.wantSafety() and
  12521             operand_ty.zigTypeTag(zcu) == .@"enum" and
  12522             (!operand_ty.isNonexhaustiveEnum(zcu) or union_originally))
  12523         {
  12524             try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
  12525             const ok = try case_block.addUnOp(.is_named_enum_value, operand);
  12526             try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
  12527         }
  12528 
  12529         const analyze_body = if (union_originally and !special.is_inline)
  12530             for (seen_enum_fields, 0..) |seen_field, index| {
  12531                 if (seen_field != null) continue;
  12532                 const union_obj = zcu.typeToUnion(maybe_union_ty).?;
  12533                 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[index]);
  12534                 if (field_ty.zigTypeTag(zcu) != .noreturn) break true;
  12535             } else false
  12536         else
  12537             true;
  12538         const else_hint: std.builtin.BranchHint = if (special.body.len != 0 and err_set and
  12539             try sema.maybeErrorUnwrap(&case_block, special.body, operand, operand_src, allow_err_code_unwrap))
  12540         h: {
  12541             // nothing to do here. weight against error branch
  12542             break :h .unlikely;
  12543         } else if (special.body.len != 0 and analyze_body and !special.is_inline) h: {
  12544             break :h try spa.analyzeProngRuntime(
  12545                 &case_block,
  12546                 .special,
  12547                 special.body,
  12548                 special.capture,
  12549                 child_block.src(.{ .switch_capture = .{
  12550                     .switch_node_offset = switch_node_offset,
  12551                     .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
  12552                 } }),
  12553                 undefined, // case_vals may be undefined for special prongs
  12554                 .none,
  12555                 false,
  12556             );
  12557         } else h: {
  12558             // We still need a terminator in this block, but we have proven
  12559             // that it is unreachable.
  12560             if (case_block.wantSafety()) {
  12561                 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
  12562                 try sema.safetyPanic(&case_block, src, .corrupt_switch);
  12563             } else {
  12564                 _ = try case_block.addNoOp(.unreach);
  12565             }
  12566             // Safety check / unreachable branches are cold.
  12567             break :h .cold;
  12568         };
  12569 
  12570         try branch_hints.append(gpa, else_hint);
  12571         break :else_body case_block.instructions.items;
  12572     } else else_body: {
  12573         try branch_hints.append(gpa, .none);
  12574         break :else_body &.{};
  12575     };
  12576 
  12577     assert(branch_hints.items.len == cases_len + 1);
  12578 
  12579     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".fields.len +
  12580         cases_extra.items.len + else_body.len +
  12581         (std.math.divCeil(usize, branch_hints.items.len, 10) catch unreachable)); // branch hints
  12582 
  12583     const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
  12584         .cases_len = @intCast(cases_len),
  12585         .else_body_len = @intCast(else_body.len),
  12586     });
  12587 
  12588     {
  12589         // Add branch hints.
  12590         var cur_bag: u32 = 0;
  12591         for (branch_hints.items, 0..) |hint, idx| {
  12592             const idx_in_bag = idx % 10;
  12593             cur_bag |= @as(u32, @intFromEnum(hint)) << @intCast(idx_in_bag * 3);
  12594             if (idx_in_bag == 9) {
  12595                 sema.air_extra.appendAssumeCapacity(cur_bag);
  12596                 cur_bag = 0;
  12597             }
  12598         }
  12599         if (branch_hints.items.len % 10 != 0) {
  12600             sema.air_extra.appendAssumeCapacity(cur_bag);
  12601         }
  12602     }
  12603     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cases_extra.items));
  12604     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_body));
  12605 
  12606     const has_any_continues = spa.operand == .loop and child_block.label.?.merges.extra_insts.items.len > 0;
  12607 
  12608     return try child_block.addInst(.{
  12609         .tag = if (has_any_continues) .loop_switch_br else .switch_br,
  12610         .data = .{ .pl_op = .{
  12611             .operand = operand,
  12612             .payload = payload_index,
  12613         } },
  12614     });
  12615 }
  12616 
  12617 fn resolveSwitchComptimeLoop(
  12618     sema: *Sema,
  12619     init_spa: SwitchProngAnalysis,
  12620     child_block: *Block,
  12621     maybe_ptr_operand_ty: Type,
  12622     cond_ty: Type,
  12623     init_cond_val: Value,
  12624     switch_node_offset: std.zig.Ast.Node.Offset,
  12625     special: SpecialProng,
  12626     case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
  12627     scalar_cases_len: u32,
  12628     multi_cases_len: u32,
  12629     err_set: bool,
  12630     empty_enum: bool,
  12631     operand_is_ref: bool,
  12632 ) CompileError!Air.Inst.Ref {
  12633     var spa = init_spa;
  12634     var cond_val = init_cond_val;
  12635 
  12636     while (true) {
  12637         if (resolveSwitchComptime(
  12638             sema,
  12639             spa,
  12640             child_block,
  12641             spa.operand.simple.cond,
  12642             cond_val,
  12643             cond_ty,
  12644             switch_node_offset,
  12645             special,
  12646             case_vals,
  12647             scalar_cases_len,
  12648             multi_cases_len,
  12649             err_set,
  12650             empty_enum,
  12651         )) |result| {
  12652             return result;
  12653         } else |err| switch (err) {
  12654             error.ComptimeBreak => {
  12655                 const break_inst = sema.code.instructions.get(@intFromEnum(sema.comptime_break_inst));
  12656                 if (break_inst.tag != .switch_continue) return error.ComptimeBreak;
  12657                 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data;
  12658                 if (extra.block_inst != spa.switch_block_inst) return error.ComptimeBreak;
  12659                 // This is a `switch_continue` targeting this block. Change the operand and start over.
  12660                 const src = child_block.nodeOffset(extra.operand_src_node.unwrap().?);
  12661                 const new_operand_uncoerced = try sema.resolveInst(break_inst.data.@"break".operand);
  12662                 const new_operand = try sema.coerce(child_block, maybe_ptr_operand_ty, new_operand_uncoerced, src);
  12663 
  12664                 try sema.emitBackwardBranch(child_block, src);
  12665 
  12666                 const val, const ref = if (operand_is_ref)
  12667                     .{ try sema.analyzeLoad(child_block, src, new_operand, src), new_operand }
  12668                 else
  12669                     .{ new_operand, undefined };
  12670 
  12671                 const cond_ref = try sema.switchCond(child_block, src, val);
  12672 
  12673                 cond_val = try sema.resolveConstDefinedValue(child_block, src, cond_ref, null);
  12674                 spa.operand = .{ .simple = .{
  12675                     .by_val = val,
  12676                     .by_ref = ref,
  12677                     .cond = cond_ref,
  12678                 } };
  12679             },
  12680             else => |e| return e,
  12681         }
  12682     }
  12683 }
  12684 
  12685 fn resolveSwitchComptime(
  12686     sema: *Sema,
  12687     spa: SwitchProngAnalysis,
  12688     child_block: *Block,
  12689     cond_operand: Air.Inst.Ref,
  12690     operand_val: Value,
  12691     operand_ty: Type,
  12692     switch_node_offset: std.zig.Ast.Node.Offset,
  12693     special: SpecialProng,
  12694     case_vals: std.ArrayListUnmanaged(Air.Inst.Ref),
  12695     scalar_cases_len: u32,
  12696     multi_cases_len: u32,
  12697     err_set: bool,
  12698     empty_enum: bool,
  12699 ) CompileError!Air.Inst.Ref {
  12700     const merges = &child_block.label.?.merges;
  12701     const resolved_operand_val = try sema.resolveLazyValue(operand_val);
  12702 
  12703     var extra_index: usize = special.end;
  12704     {
  12705         var scalar_i: usize = 0;
  12706         while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  12707             extra_index += 1;
  12708             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  12709             extra_index += 1;
  12710             const body = sema.code.bodySlice(extra_index, info.body_len);
  12711             extra_index += info.body_len;
  12712 
  12713             const item = case_vals.items[scalar_i];
  12714             const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  12715             if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) {
  12716                 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
  12717                 return spa.resolveProngComptime(
  12718                     child_block,
  12719                     .normal,
  12720                     body,
  12721                     info.capture,
  12722                     child_block.src(.{ .switch_capture = .{
  12723                         .switch_node_offset = switch_node_offset,
  12724                         .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  12725                     } }),
  12726                     &.{item},
  12727                     if (info.is_inline) cond_operand else .none,
  12728                     info.has_tag_capture,
  12729                     merges,
  12730                 );
  12731             }
  12732         }
  12733     }
  12734     {
  12735         var multi_i: usize = 0;
  12736         var case_val_idx: usize = scalar_cases_len;
  12737         while (multi_i < multi_cases_len) : (multi_i += 1) {
  12738             const items_len = sema.code.extra[extra_index];
  12739             extra_index += 1;
  12740             const ranges_len = sema.code.extra[extra_index];
  12741             extra_index += 1;
  12742             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  12743             extra_index += 1 + items_len;
  12744             const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len);
  12745 
  12746             const items = case_vals.items[case_val_idx..][0..items_len];
  12747             case_val_idx += items_len;
  12748 
  12749             for (items) |item| {
  12750                 // Validation above ensured these will succeed.
  12751                 const item_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, item, undefined) catch unreachable;
  12752                 if (operand_val.eql(item_val, operand_ty, sema.pt.zcu)) {
  12753                     if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
  12754                     return spa.resolveProngComptime(
  12755                         child_block,
  12756                         .normal,
  12757                         body,
  12758                         info.capture,
  12759                         child_block.src(.{ .switch_capture = .{
  12760                             .switch_node_offset = switch_node_offset,
  12761                             .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12762                         } }),
  12763                         items,
  12764                         if (info.is_inline) cond_operand else .none,
  12765                         info.has_tag_capture,
  12766                         merges,
  12767                     );
  12768                 }
  12769             }
  12770 
  12771             var range_i: usize = 0;
  12772             while (range_i < ranges_len) : (range_i += 1) {
  12773                 const range_items = case_vals.items[case_val_idx..][0..2];
  12774                 extra_index += 2;
  12775                 case_val_idx += 2;
  12776 
  12777                 // Validation above ensured these will succeed.
  12778                 const first_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[0], undefined) catch unreachable;
  12779                 const last_val = sema.resolveConstDefinedValue(child_block, LazySrcLoc.unneeded, range_items[1], undefined) catch unreachable;
  12780                 if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and
  12781                     (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty)))
  12782                 {
  12783                     if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_operand);
  12784                     return spa.resolveProngComptime(
  12785                         child_block,
  12786                         .normal,
  12787                         body,
  12788                         info.capture,
  12789                         child_block.src(.{ .switch_capture = .{
  12790                             .switch_node_offset = switch_node_offset,
  12791                             .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12792                         } }),
  12793                         undefined, // case_vals may be undefined for ranges
  12794                         if (info.is_inline) cond_operand else .none,
  12795                         info.has_tag_capture,
  12796                         merges,
  12797                     );
  12798                 }
  12799             }
  12800 
  12801             extra_index += info.body_len;
  12802         }
  12803     }
  12804     if (err_set) try sema.maybeErrorUnwrapComptime(child_block, special.body, cond_operand);
  12805     if (empty_enum) {
  12806         return .void_value;
  12807     }
  12808 
  12809     return spa.resolveProngComptime(
  12810         child_block,
  12811         .special,
  12812         special.body,
  12813         special.capture,
  12814         child_block.src(.{ .switch_capture = .{
  12815             .switch_node_offset = switch_node_offset,
  12816             .case_idx = LazySrcLoc.Offset.SwitchCaseIndex.special,
  12817         } }),
  12818         undefined, // case_vals may be undefined for special prongs
  12819         if (special.is_inline) cond_operand else .none,
  12820         special.has_tag_capture,
  12821         merges,
  12822     );
  12823 }
  12824 
  12825 const RangeSetUnhandledIterator = struct {
  12826     pt: Zcu.PerThread,
  12827     cur: ?InternPool.Index,
  12828     max: InternPool.Index,
  12829     range_i: usize,
  12830     ranges: []const RangeSet.Range,
  12831     limbs: []math.big.Limb,
  12832 
  12833     const preallocated_limbs = math.big.int.calcTwosCompLimbCount(128);
  12834 
  12835     fn init(sema: *Sema, ty: Type, range_set: RangeSet) !RangeSetUnhandledIterator {
  12836         const pt = sema.pt;
  12837         const int_type = pt.zcu.intern_pool.indexToKey(ty.toIntern()).int_type;
  12838         const needed_limbs = math.big.int.calcTwosCompLimbCount(int_type.bits);
  12839         return .{
  12840             .pt = pt,
  12841             .cur = (try ty.minInt(pt, ty)).toIntern(),
  12842             .max = (try ty.maxInt(pt, ty)).toIntern(),
  12843             .range_i = 0,
  12844             .ranges = range_set.ranges.items,
  12845             .limbs = if (needed_limbs > preallocated_limbs)
  12846                 try sema.arena.alloc(math.big.Limb, needed_limbs)
  12847             else
  12848                 &.{},
  12849         };
  12850     }
  12851 
  12852     fn addOne(it: *const RangeSetUnhandledIterator, val: InternPool.Index) !?InternPool.Index {
  12853         if (val == it.max) return null;
  12854         const int = it.pt.zcu.intern_pool.indexToKey(val).int;
  12855 
  12856         switch (int.storage) {
  12857             inline .u64, .i64 => |val_int| {
  12858                 const next_int = @addWithOverflow(val_int, 1);
  12859                 if (next_int[1] == 0)
  12860                     return (try it.pt.intValue(.fromInterned(int.ty), next_int[0])).toIntern();
  12861             },
  12862             .big_int => {},
  12863             .lazy_align, .lazy_size => unreachable,
  12864         }
  12865 
  12866         var val_space: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  12867         const val_bigint = int.storage.toBigInt(&val_space);
  12868 
  12869         var result_limbs: [preallocated_limbs]math.big.Limb = undefined;
  12870         var result_bigint = math.big.int.Mutable.init(
  12871             if (it.limbs.len > 0) it.limbs else &result_limbs,
  12872             0,
  12873         );
  12874 
  12875         result_bigint.addScalar(val_bigint, 1);
  12876         return (try it.pt.intValue_big(.fromInterned(int.ty), result_bigint.toConst())).toIntern();
  12877     }
  12878 
  12879     fn next(it: *RangeSetUnhandledIterator) !?InternPool.Index {
  12880         var cur = it.cur orelse return null;
  12881         while (it.range_i < it.ranges.len and cur == it.ranges[it.range_i].first) {
  12882             defer it.range_i += 1;
  12883             cur = (try it.addOne(it.ranges[it.range_i].last)) orelse {
  12884                 it.cur = null;
  12885                 return null;
  12886             };
  12887         }
  12888         it.cur = try it.addOne(cur);
  12889         return cur;
  12890     }
  12891 };
  12892 
  12893 const ResolvedSwitchItem = struct {
  12894     ref: Air.Inst.Ref,
  12895     val: InternPool.Index,
  12896 };
  12897 fn resolveSwitchItemVal(
  12898     sema: *Sema,
  12899     block: *Block,
  12900     item_ref: Zir.Inst.Ref,
  12901     /// Coerce `item_ref` to this type.
  12902     coerce_ty: Type,
  12903     item_src: LazySrcLoc,
  12904 ) CompileError!ResolvedSwitchItem {
  12905     const uncoerced_item = try sema.resolveInst(item_ref);
  12906 
  12907     // Constructing a LazySrcLoc is costly because we only have the switch AST node.
  12908     // Only if we know for sure we need to report a compile error do we resolve the
  12909     // full source locations.
  12910 
  12911     const item = try sema.coerce(block, coerce_ty, uncoerced_item, item_src);
  12912 
  12913     const maybe_lazy = try sema.resolveConstDefinedValue(block, item_src, item, .{ .simple = .switch_item });
  12914 
  12915     const val = try sema.resolveLazyValue(maybe_lazy);
  12916     const new_item = if (val.toIntern() != maybe_lazy.toIntern()) blk: {
  12917         break :blk Air.internedToRef(val.toIntern());
  12918     } else item;
  12919 
  12920     return .{ .ref = new_item, .val = val.toIntern() };
  12921 }
  12922 
  12923 fn validateErrSetSwitch(
  12924     sema: *Sema,
  12925     block: *Block,
  12926     seen_errors: *SwitchErrorSet,
  12927     case_vals: *std.ArrayListUnmanaged(Air.Inst.Ref),
  12928     operand_ty: Type,
  12929     inst_data: @FieldType(Zir.Inst.Data, "pl_node"),
  12930     scalar_cases_len: u32,
  12931     multi_cases_len: u32,
  12932     else_case: struct { body: []const Zir.Inst.Index, end: usize, src: LazySrcLoc },
  12933     has_else: bool,
  12934 ) CompileError!?Type {
  12935     const gpa = sema.gpa;
  12936     const pt = sema.pt;
  12937     const zcu = pt.zcu;
  12938     const ip = &zcu.intern_pool;
  12939 
  12940     const src_node_offset = inst_data.src_node;
  12941     const src = block.nodeOffset(src_node_offset);
  12942 
  12943     var extra_index: usize = else_case.end;
  12944     {
  12945         var scalar_i: u32 = 0;
  12946         while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
  12947             const item_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  12948             extra_index += 1;
  12949             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  12950             extra_index += 1 + info.body_len;
  12951 
  12952             case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
  12953                 block,
  12954                 seen_errors,
  12955                 item_ref,
  12956                 operand_ty,
  12957                 block.src(.{ .switch_case_item = .{
  12958                     .switch_node_offset = src_node_offset,
  12959                     .case_idx = .{ .kind = .scalar, .index = @intCast(scalar_i) },
  12960                     .item_idx = .{ .kind = .single, .index = 0 },
  12961                 } }),
  12962             ));
  12963         }
  12964     }
  12965     {
  12966         var multi_i: u32 = 0;
  12967         while (multi_i < multi_cases_len) : (multi_i += 1) {
  12968             const items_len = sema.code.extra[extra_index];
  12969             extra_index += 1;
  12970             const ranges_len = sema.code.extra[extra_index];
  12971             extra_index += 1;
  12972             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
  12973             extra_index += 1;
  12974             const items = sema.code.refSlice(extra_index, items_len);
  12975             extra_index += items_len + info.body_len;
  12976 
  12977             try case_vals.ensureUnusedCapacity(gpa, items.len);
  12978             for (items, 0..) |item_ref, item_i| {
  12979                 case_vals.appendAssumeCapacity(try sema.validateSwitchItemError(
  12980                     block,
  12981                     seen_errors,
  12982                     item_ref,
  12983                     operand_ty,
  12984                     block.src(.{ .switch_case_item = .{
  12985                         .switch_node_offset = src_node_offset,
  12986                         .case_idx = .{ .kind = .multi, .index = @intCast(multi_i) },
  12987                         .item_idx = .{ .kind = .single, .index = @intCast(item_i) },
  12988                     } }),
  12989                 ));
  12990             }
  12991 
  12992             try sema.validateSwitchNoRange(block, ranges_len, operand_ty, src_node_offset);
  12993         }
  12994     }
  12995 
  12996     switch (try sema.resolveInferredErrorSetTy(block, src, operand_ty.toIntern())) {
  12997         .anyerror_type => {
  12998             if (!has_else) {
  12999                 return sema.fail(
  13000                     block,
  13001                     src,
  13002                     "else prong required when switching on type 'anyerror'",
  13003                     .{},
  13004                 );
  13005             }
  13006             return .anyerror;
  13007         },
  13008         else => |err_set_ty_index| else_validation: {
  13009             const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
  13010             var maybe_msg: ?*Zcu.ErrorMsg = null;
  13011             errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
  13012 
  13013             for (error_names.get(ip)) |error_name| {
  13014                 if (!seen_errors.contains(error_name) and !has_else) {
  13015                     const msg = maybe_msg orelse blk: {
  13016                         maybe_msg = try sema.errMsg(
  13017                             src,
  13018                             "switch must handle all possibilities",
  13019                             .{},
  13020                         );
  13021                         break :blk maybe_msg.?;
  13022                     };
  13023 
  13024                     try sema.errNote(
  13025                         src,
  13026                         msg,
  13027                         "unhandled error value: 'error.{f}'",
  13028                         .{error_name.fmt(ip)},
  13029                     );
  13030                 }
  13031             }
  13032 
  13033             if (maybe_msg) |msg| {
  13034                 maybe_msg = null;
  13035                 try sema.addDeclaredHereNote(msg, operand_ty);
  13036                 return sema.failWithOwnedErrorMsg(block, msg);
  13037             }
  13038 
  13039             if (has_else and seen_errors.count() == error_names.len) {
  13040                 // In order to enable common patterns for generic code allow simple else bodies
  13041                 // else => unreachable,
  13042                 // else => return,
  13043                 // else => |e| return e,
  13044                 // even if all the possible errors were already handled.
  13045                 const tags = sema.code.instructions.items(.tag);
  13046                 const datas = sema.code.instructions.items(.data);
  13047                 for (else_case.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) {
  13048                     .dbg_stmt,
  13049                     .dbg_var_val,
  13050                     .ret_type,
  13051                     .as_node,
  13052                     .ret_node,
  13053                     .@"unreachable",
  13054                     .@"defer",
  13055                     .defer_err_code,
  13056                     .err_union_code,
  13057                     .ret_err_value_code,
  13058                     .save_err_ret_index,
  13059                     .restore_err_ret_index_unconditional,
  13060                     .restore_err_ret_index_fn_entry,
  13061                     .is_non_err,
  13062                     .ret_is_non_err,
  13063                     .condbr,
  13064                     => {},
  13065                     .extended => switch (datas[@intFromEnum(else_inst)].extended.opcode) {
  13066                         .restore_err_ret_index => {},
  13067                         else => break,
  13068                     },
  13069                     else => break,
  13070                 } else break :else_validation;
  13071 
  13072                 return sema.fail(
  13073                     block,
  13074                     else_case.src,
  13075                     "unreachable else prong; all cases already handled",
  13076                     .{},
  13077                 );
  13078             }
  13079 
  13080             var names: InferredErrorSet.NameMap = .{};
  13081             try names.ensureUnusedCapacity(sema.arena, error_names.len);
  13082             for (error_names.get(ip)) |error_name| {
  13083                 if (seen_errors.contains(error_name)) continue;
  13084 
  13085                 names.putAssumeCapacityNoClobber(error_name, {});
  13086             }
  13087             // No need to keep the hash map metadata correct; here we
  13088             // extract the (sorted) keys only.
  13089             return try pt.errorSetFromUnsortedNames(names.keys());
  13090         },
  13091     }
  13092     return null;
  13093 }
  13094 
  13095 fn validateSwitchRange(
  13096     sema: *Sema,
  13097     block: *Block,
  13098     range_set: *RangeSet,
  13099     first_ref: Zir.Inst.Ref,
  13100     last_ref: Zir.Inst.Ref,
  13101     operand_ty: Type,
  13102     item_src: LazySrcLoc,
  13103 ) CompileError![2]Air.Inst.Ref {
  13104     const first_src: LazySrcLoc = .{
  13105         .base_node_inst = item_src.base_node_inst,
  13106         .offset = .{ .switch_case_item_range_first = item_src.offset.switch_case_item },
  13107     };
  13108     const last_src: LazySrcLoc = .{
  13109         .base_node_inst = item_src.base_node_inst,
  13110         .offset = .{ .switch_case_item_range_last = item_src.offset.switch_case_item },
  13111     };
  13112     const first = try sema.resolveSwitchItemVal(block, first_ref, operand_ty, first_src);
  13113     const last = try sema.resolveSwitchItemVal(block, last_ref, operand_ty, last_src);
  13114     if (try Value.fromInterned(first.val).compareAll(.gt, Value.fromInterned(last.val), operand_ty, sema.pt)) {
  13115         return sema.fail(block, item_src, "range start value is greater than the end value", .{});
  13116     }
  13117     const maybe_prev_src = try range_set.add(first.val, last.val, item_src);
  13118     try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13119     return .{ first.ref, last.ref };
  13120 }
  13121 
  13122 fn validateSwitchItemInt(
  13123     sema: *Sema,
  13124     block: *Block,
  13125     range_set: *RangeSet,
  13126     item_ref: Zir.Inst.Ref,
  13127     operand_ty: Type,
  13128     item_src: LazySrcLoc,
  13129 ) CompileError!Air.Inst.Ref {
  13130     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
  13131     const maybe_prev_src = try range_set.add(item.val, item.val, item_src);
  13132     try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13133     return item.ref;
  13134 }
  13135 
  13136 fn validateSwitchItemEnum(
  13137     sema: *Sema,
  13138     block: *Block,
  13139     seen_fields: []?LazySrcLoc,
  13140     range_set: *RangeSet,
  13141     item_ref: Zir.Inst.Ref,
  13142     operand_ty: Type,
  13143     item_src: LazySrcLoc,
  13144 ) CompileError!Air.Inst.Ref {
  13145     const ip = &sema.pt.zcu.intern_pool;
  13146     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
  13147     const int = ip.indexToKey(item.val).enum_tag.int;
  13148     const field_index = ip.loadEnumType(ip.typeOf(item.val)).tagValueIndex(ip, int) orelse {
  13149         const maybe_prev_src = try range_set.add(int, int, item_src);
  13150         try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13151         return item.ref;
  13152     };
  13153     const maybe_prev_src = seen_fields[field_index];
  13154     seen_fields[field_index] = item_src;
  13155     try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13156     return item.ref;
  13157 }
  13158 
  13159 fn validateSwitchItemError(
  13160     sema: *Sema,
  13161     block: *Block,
  13162     seen_errors: *SwitchErrorSet,
  13163     item_ref: Zir.Inst.Ref,
  13164     operand_ty: Type,
  13165     item_src: LazySrcLoc,
  13166 ) CompileError!Air.Inst.Ref {
  13167     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
  13168     const error_name = sema.pt.zcu.intern_pool.indexToKey(item.val).err.name;
  13169     const maybe_prev_src = if (try seen_errors.fetchPut(error_name, item_src)) |prev|
  13170         prev.value
  13171     else
  13172         null;
  13173     try sema.validateSwitchDupe(block, maybe_prev_src, item_src);
  13174     return item.ref;
  13175 }
  13176 
  13177 fn validateSwitchDupe(
  13178     sema: *Sema,
  13179     block: *Block,
  13180     maybe_prev_src: ?LazySrcLoc,
  13181     item_src: LazySrcLoc,
  13182 ) CompileError!void {
  13183     const prev_item_src = maybe_prev_src orelse return;
  13184     return sema.failWithOwnedErrorMsg(block, msg: {
  13185         const msg = try sema.errMsg(
  13186             item_src,
  13187             "duplicate switch value",
  13188             .{},
  13189         );
  13190         errdefer msg.destroy(sema.gpa);
  13191         try sema.errNote(
  13192             prev_item_src,
  13193             msg,
  13194             "previous value here",
  13195             .{},
  13196         );
  13197         break :msg msg;
  13198     });
  13199 }
  13200 
  13201 fn validateSwitchItemBool(
  13202     sema: *Sema,
  13203     block: *Block,
  13204     true_count: *u8,
  13205     false_count: *u8,
  13206     item_ref: Zir.Inst.Ref,
  13207     item_src: LazySrcLoc,
  13208 ) CompileError!Air.Inst.Ref {
  13209     const item = try sema.resolveSwitchItemVal(block, item_ref, .bool, item_src);
  13210     if (Value.fromInterned(item.val).toBool()) {
  13211         true_count.* += 1;
  13212     } else {
  13213         false_count.* += 1;
  13214     }
  13215     if (true_count.* > 1 or false_count.* > 1) {
  13216         return sema.fail(block, item_src, "duplicate switch value", .{});
  13217     }
  13218     return item.ref;
  13219 }
  13220 
  13221 const ValueSrcMap = std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc);
  13222 
  13223 fn validateSwitchItemSparse(
  13224     sema: *Sema,
  13225     block: *Block,
  13226     seen_values: *ValueSrcMap,
  13227     item_ref: Zir.Inst.Ref,
  13228     operand_ty: Type,
  13229     item_src: LazySrcLoc,
  13230 ) CompileError!Air.Inst.Ref {
  13231     const item = try sema.resolveSwitchItemVal(block, item_ref, operand_ty, item_src);
  13232     const kv = try seen_values.fetchPut(sema.gpa, item.val, item_src) orelse return item.ref;
  13233     try sema.validateSwitchDupe(block, kv.value, item_src);
  13234     unreachable;
  13235 }
  13236 
  13237 fn validateSwitchNoRange(
  13238     sema: *Sema,
  13239     block: *Block,
  13240     ranges_len: u32,
  13241     operand_ty: Type,
  13242     src_node_offset: std.zig.Ast.Node.Offset,
  13243 ) CompileError!void {
  13244     if (ranges_len == 0)
  13245         return;
  13246 
  13247     const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
  13248     const range_src = block.src(.{ .node_offset_switch_range = src_node_offset });
  13249 
  13250     const msg = msg: {
  13251         const msg = try sema.errMsg(
  13252             operand_src,
  13253             "ranges not allowed when switching on type '{f}'",
  13254             .{operand_ty.fmt(sema.pt)},
  13255         );
  13256         errdefer msg.destroy(sema.gpa);
  13257         try sema.errNote(
  13258             range_src,
  13259             msg,
  13260             "range here",
  13261             .{},
  13262         );
  13263         break :msg msg;
  13264     };
  13265     return sema.failWithOwnedErrorMsg(block, msg);
  13266 }
  13267 
  13268 fn maybeErrorUnwrap(
  13269     sema: *Sema,
  13270     block: *Block,
  13271     body: []const Zir.Inst.Index,
  13272     operand: Air.Inst.Ref,
  13273     operand_src: LazySrcLoc,
  13274     allow_err_code_inst: bool,
  13275 ) !bool {
  13276     const pt = sema.pt;
  13277     const zcu = pt.zcu;
  13278 
  13279     const tags = sema.code.instructions.items(.tag);
  13280     for (body) |inst| {
  13281         switch (tags[@intFromEnum(inst)]) {
  13282             .@"unreachable" => if (!block.wantSafety()) return false,
  13283             .err_union_code => if (!allow_err_code_inst) return false,
  13284             .save_err_ret_index,
  13285             .dbg_stmt,
  13286             .str,
  13287             .as_node,
  13288             .panic,
  13289             .field_val,
  13290             => {},
  13291             else => return false,
  13292         }
  13293     }
  13294 
  13295     for (body) |inst| {
  13296         const air_inst = switch (tags[@intFromEnum(inst)]) {
  13297             .err_union_code => continue,
  13298             .dbg_stmt => {
  13299                 try sema.zirDbgStmt(block, inst);
  13300                 continue;
  13301             },
  13302             .save_err_ret_index => {
  13303                 try sema.zirSaveErrRetIndex(block, inst);
  13304                 continue;
  13305             },
  13306             .str => try sema.zirStr(inst),
  13307             .as_node => try sema.zirAsNode(block, inst),
  13308             .field_val => try sema.zirFieldVal(block, inst),
  13309             .@"unreachable" => {
  13310                 try safetyPanicUnwrapError(sema, block, operand_src, operand);
  13311                 return true;
  13312             },
  13313             .panic => {
  13314                 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  13315                 const msg_inst = try sema.resolveInst(inst_data.operand);
  13316 
  13317                 const panic_fn = try getBuiltin(sema, operand_src, .@"panic.call");
  13318                 const args: [2]Air.Inst.Ref = .{ msg_inst, .null_value };
  13319                 try sema.callBuiltin(block, operand_src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check");
  13320                 return true;
  13321             },
  13322             else => unreachable,
  13323         };
  13324         if (sema.typeOf(air_inst).isNoReturn(zcu))
  13325             return true;
  13326         sema.inst_map.putAssumeCapacity(inst, air_inst);
  13327     }
  13328     unreachable;
  13329 }
  13330 
  13331 fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void {
  13332     const pt = sema.pt;
  13333     const zcu = pt.zcu;
  13334     const index = cond.toIndex() orelse return;
  13335     if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) return;
  13336 
  13337     const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node;
  13338     const err_operand = try sema.resolveInst(err_inst_data.operand);
  13339     const operand_ty = sema.typeOf(err_operand);
  13340     if (operand_ty.zigTypeTag(zcu) == .error_set) {
  13341         try sema.maybeErrorUnwrapComptime(block, body, err_operand);
  13342         return;
  13343     }
  13344     if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| {
  13345         if (!operand_ty.isError(zcu)) return;
  13346         if (val.getErrorName(zcu) == .none) return;
  13347         try sema.maybeErrorUnwrapComptime(block, body, err_operand);
  13348     }
  13349 }
  13350 
  13351 fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void {
  13352     const tags = sema.code.instructions.items(.tag);
  13353     const inst = for (body) |inst| {
  13354         switch (tags[@intFromEnum(inst)]) {
  13355             .dbg_stmt,
  13356             .save_err_ret_index,
  13357             => {},
  13358             .@"unreachable" => break inst,
  13359             else => return,
  13360         }
  13361     } else return;
  13362     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
  13363     const src = block.nodeOffset(inst_data.src_node);
  13364 
  13365     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
  13366         if (val.getErrorName(sema.pt.zcu).unwrap()) |name| {
  13367             return sema.failWithComptimeErrorRetTrace(block, src, name);
  13368         }
  13369     }
  13370 }
  13371 
  13372 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13373     const pt = sema.pt;
  13374     const zcu = pt.zcu;
  13375     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13376     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13377     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  13378     const name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  13379     const ty = try sema.resolveType(block, ty_src, extra.lhs);
  13380     const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name });
  13381     try ty.resolveFields(pt);
  13382     const ip = &zcu.intern_pool;
  13383 
  13384     const has_field = hf: {
  13385         switch (ip.indexToKey(ty.toIntern())) {
  13386             .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  13387                 .slice => {
  13388                     if (field_name.eqlSlice("ptr", ip)) break :hf true;
  13389                     if (field_name.eqlSlice("len", ip)) break :hf true;
  13390                     break :hf false;
  13391                 },
  13392                 else => {},
  13393             },
  13394             .tuple_type => |tuple| {
  13395                 const field_index = field_name.toUnsigned(ip) orelse break :hf false;
  13396                 break :hf field_index < tuple.types.len;
  13397             },
  13398             .struct_type => {
  13399                 break :hf ip.loadStructType(ty.toIntern()).nameIndex(ip, field_name) != null;
  13400             },
  13401             .union_type => {
  13402                 const union_type = ip.loadUnionType(ty.toIntern());
  13403                 break :hf union_type.loadTagType(ip).nameIndex(ip, field_name) != null;
  13404             },
  13405             .enum_type => {
  13406                 break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null;
  13407             },
  13408             .array_type => break :hf field_name.eqlSlice("len", ip),
  13409             else => {},
  13410         }
  13411         return sema.fail(block, ty_src, "type '{f}' does not support '@hasField'", .{
  13412             ty.fmt(pt),
  13413         });
  13414     };
  13415     return if (has_field) .bool_true else .bool_false;
  13416 }
  13417 
  13418 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13419     const pt = sema.pt;
  13420     const zcu = pt.zcu;
  13421     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13422     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13423     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  13424     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  13425     const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
  13426     const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{ .simple = .decl_name });
  13427 
  13428     try sema.checkNamespaceType(block, lhs_src, container_type);
  13429 
  13430     const namespace = container_type.getNamespace(zcu).unwrap() orelse return .bool_false;
  13431     if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| {
  13432         if (lookup.accessible) {
  13433             return .bool_true;
  13434         }
  13435     }
  13436     return .bool_false;
  13437 }
  13438 
  13439 fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13440     const tracy = trace(@src());
  13441     defer tracy.end();
  13442 
  13443     const pt = sema.pt;
  13444     const zcu = pt.zcu;
  13445     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
  13446     const extra = sema.code.extraData(Zir.Inst.Import, inst_data.payload_index).data;
  13447     const operand_src = block.tokenOffset(inst_data.src_tok);
  13448     const operand = sema.code.nullTerminatedString(extra.path);
  13449 
  13450     const result = pt.doImport(block.getFileScope(zcu), operand) catch |err| switch (err) {
  13451         error.ModuleNotFound => return sema.fail(block, operand_src, "no module named '{s}' available within module '{s}'", .{
  13452             operand, block.getFileScope(zcu).mod.?.fully_qualified_name,
  13453         }),
  13454         error.IllegalZigImport => unreachable, // caught before semantic analysis
  13455         error.OutOfMemory => |e| return e,
  13456     };
  13457     const file_index = result.file;
  13458     const file = zcu.fileByIndex(file_index);
  13459     switch (file.getMode()) {
  13460         .zig => {
  13461             try pt.ensureFileAnalyzed(file_index);
  13462             const ty = zcu.fileRootType(file_index);
  13463             try sema.declareDependency(.{ .interned = ty });
  13464             try sema.addTypeReferenceEntry(operand_src, ty);
  13465             return Air.internedToRef(ty);
  13466         },
  13467         .zon => {
  13468             const res_ty: InternPool.Index = b: {
  13469                 if (extra.res_ty == .none) break :b .none;
  13470                 const res_ty_inst = try sema.resolveInst(extra.res_ty);
  13471                 const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst);
  13472                 if (res_ty.isGenericPoison()) break :b .none;
  13473                 break :b res_ty.toIntern();
  13474             };
  13475 
  13476             try sema.declareDependency(.{ .zon_file = file_index });
  13477             const interned = try LowerZon.run(
  13478                 sema,
  13479                 file,
  13480                 file_index,
  13481                 res_ty,
  13482                 operand_src,
  13483                 block,
  13484             );
  13485             return Air.internedToRef(interned);
  13486         },
  13487     }
  13488 }
  13489 
  13490 fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13491     const tracy = trace(@src());
  13492     defer tracy.end();
  13493 
  13494     const pt = sema.pt;
  13495     const zcu = pt.zcu;
  13496 
  13497     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  13498     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  13499     const name = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .operand_embedFile });
  13500 
  13501     if (name.len == 0) {
  13502         return sema.fail(block, operand_src, "file path name cannot be empty", .{});
  13503     }
  13504 
  13505     const ef_idx = pt.embedFile(block.getFileScope(zcu), name) catch |err| switch (err) {
  13506         error.ImportOutsideModulePath => {
  13507             return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name});
  13508         },
  13509         error.CurrentWorkingDirectoryUnlinked => {
  13510             // TODO: this should be some kind of retryable failure, in case the cwd is put back
  13511             return sema.fail(block, operand_src, "unable to resolve '{s}': working directory has been unlinked", .{name});
  13512         },
  13513         error.OutOfMemory => |e| return e,
  13514     };
  13515     try sema.declareDependency(.{ .embed_file = ef_idx });
  13516 
  13517     const result = ef_idx.get(zcu);
  13518     if (result.val == .none) {
  13519         return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(result.err.?) });
  13520     }
  13521 
  13522     return Air.internedToRef(result.val);
  13523 }
  13524 
  13525 fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13526     const pt = sema.pt;
  13527     const zcu = pt.zcu;
  13528     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
  13529     const name = try zcu.intern_pool.getOrPutString(
  13530         sema.gpa,
  13531         pt.tid,
  13532         inst_data.get(sema.code),
  13533         .no_embedded_nulls,
  13534     );
  13535     _ = try pt.getErrorValue(name);
  13536     const error_set_type = try pt.singleErrorSetType(name);
  13537     return Air.internedToRef((try pt.intern(.{ .err = .{
  13538         .ty = error_set_type.toIntern(),
  13539         .name = name,
  13540     } })));
  13541 }
  13542 
  13543 fn zirShl(
  13544     sema: *Sema,
  13545     block: *Block,
  13546     inst: Zir.Inst.Index,
  13547     air_tag: Air.Inst.Tag,
  13548 ) CompileError!Air.Inst.Ref {
  13549     const tracy = trace(@src());
  13550     defer tracy.end();
  13551 
  13552     const pt = sema.pt;
  13553     const zcu = pt.zcu;
  13554     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13555     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13556     const lhs = try sema.resolveInst(extra.lhs);
  13557     const rhs = try sema.resolveInst(extra.rhs);
  13558     const lhs_ty = sema.typeOf(lhs);
  13559     const rhs_ty = sema.typeOf(rhs);
  13560 
  13561     const src = block.nodeOffset(inst_data.src_node);
  13562     const lhs_src, const rhs_src = switch (air_tag) {
  13563         .shl, .shl_sat => .{
  13564             block.src(.{ .node_offset_bin_lhs = inst_data.src_node }),
  13565             block.src(.{ .node_offset_bin_rhs = inst_data.src_node }),
  13566         },
  13567         .shl_exact => .{
  13568             block.builtinCallArgSrc(inst_data.src_node, 0),
  13569             block.builtinCallArgSrc(inst_data.src_node, 1),
  13570         },
  13571         else => unreachable,
  13572     };
  13573 
  13574     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13575 
  13576     const scalar_ty = lhs_ty.scalarType(zcu);
  13577     const scalar_rhs_ty = rhs_ty.scalarType(zcu);
  13578 
  13579     _ = try sema.checkIntType(block, rhs_src, scalar_rhs_ty);
  13580 
  13581     const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs);
  13582     const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs);
  13583 
  13584     if (maybe_rhs_val) |rhs_val| {
  13585         if (rhs_val.isUndef(zcu)) {
  13586             return pt.undefRef(sema.typeOf(lhs));
  13587         }
  13588         // If rhs is 0, return lhs without doing any calculations.
  13589         if (try rhs_val.compareAllWithZeroSema(.eq, pt)) {
  13590             return lhs;
  13591         }
  13592         if (air_tag != .shl_sat and scalar_ty.zigTypeTag(zcu) != .comptime_int) {
  13593             const bit_value = try pt.intValue(.comptime_int, scalar_ty.intInfo(zcu).bits);
  13594             if (rhs_ty.zigTypeTag(zcu) == .vector) {
  13595                 var i: usize = 0;
  13596                 while (i < rhs_ty.vectorLen(zcu)) : (i += 1) {
  13597                     const rhs_elem = try rhs_val.elemValue(pt, i);
  13598                     if (rhs_elem.compareHetero(.gte, bit_value, zcu)) {
  13599                         return sema.fail(block, rhs_src, "shift amount '{f}' at index '{d}' is too large for operand type '{f}'", .{
  13600                             rhs_elem.fmtValueSema(pt, sema),
  13601                             i,
  13602                             scalar_ty.fmt(pt),
  13603                         });
  13604                     }
  13605                 }
  13606             } else if (rhs_val.compareHetero(.gte, bit_value, zcu)) {
  13607                 return sema.fail(block, rhs_src, "shift amount '{f}' is too large for operand type '{f}'", .{
  13608                     rhs_val.fmtValueSema(pt, sema),
  13609                     scalar_ty.fmt(pt),
  13610                 });
  13611             }
  13612         }
  13613         if (rhs_ty.zigTypeTag(zcu) == .vector) {
  13614             var i: usize = 0;
  13615             while (i < rhs_ty.vectorLen(zcu)) : (i += 1) {
  13616                 const rhs_elem = try rhs_val.elemValue(pt, i);
  13617                 if (rhs_elem.compareHetero(.lt, try pt.intValue(scalar_rhs_ty, 0), zcu)) {
  13618                     return sema.fail(block, rhs_src, "shift by negative amount '{f}' at index '{d}'", .{
  13619                         rhs_elem.fmtValueSema(pt, sema),
  13620                         i,
  13621                     });
  13622                 }
  13623             }
  13624         } else if (rhs_val.compareHetero(.lt, try pt.intValue(rhs_ty, 0), zcu)) {
  13625             return sema.fail(block, rhs_src, "shift by negative amount '{f}'", .{
  13626                 rhs_val.fmtValueSema(pt, sema),
  13627             });
  13628         }
  13629     } else if (scalar_rhs_ty.isSignedInt(zcu)) {
  13630         return sema.fail(block, rhs_src, "shift by signed type '{f}'", .{rhs_ty.fmt(pt)});
  13631     }
  13632 
  13633     const runtime_src = if (maybe_lhs_val) |lhs_val| rs: {
  13634         if (lhs_val.isUndef(zcu)) return pt.undefRef(lhs_ty);
  13635         const rhs_val = maybe_rhs_val orelse {
  13636             if (scalar_ty.zigTypeTag(zcu) == .comptime_int) {
  13637                 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
  13638             }
  13639             break :rs rhs_src;
  13640         };
  13641         const val = if (scalar_ty.zigTypeTag(zcu) == .comptime_int)
  13642             try lhs_val.shl(rhs_val, lhs_ty, sema.arena, pt)
  13643         else switch (air_tag) {
  13644             .shl_exact => val: {
  13645                 const shifted = try lhs_val.shlWithOverflow(rhs_val, lhs_ty, sema.arena, pt);
  13646                 if (shifted.overflow_bit.compareAllWithZero(.eq, zcu)) {
  13647                     break :val shifted.wrapped_result;
  13648                 }
  13649                 return sema.fail(block, src, "operation caused overflow", .{});
  13650             },
  13651             .shl_sat => try lhs_val.shlSat(rhs_val, lhs_ty, sema.arena, pt),
  13652             .shl => try lhs_val.shlTrunc(rhs_val, lhs_ty, sema.arena, pt),
  13653             else => unreachable,
  13654         };
  13655         return Air.internedToRef(val.toIntern());
  13656     } else lhs_src;
  13657 
  13658     const rt_rhs = switch (air_tag) {
  13659         else => unreachable,
  13660         .shl, .shl_exact => rhs,
  13661         // The backend can handle a large runtime rhs better than we can, but
  13662         // we can limit a large comptime rhs better here. This also has the
  13663         // necessary side effect of preventing rhs from being a `comptime_int`.
  13664         .shl_sat => if (maybe_rhs_val) |rhs_val| Air.internedToRef(rt_rhs: {
  13665             const bit_count = scalar_ty.intInfo(zcu).bits;
  13666             const rt_rhs_scalar_ty = try pt.smallestUnsignedInt(bit_count);
  13667             if (!rhs_ty.isVector(zcu)) break :rt_rhs (try pt.intValue(
  13668                 rt_rhs_scalar_ty,
  13669                 @min(try rhs_val.getUnsignedIntSema(pt) orelse bit_count, bit_count),
  13670             )).toIntern();
  13671             const rhs_len = rhs_ty.vectorLen(zcu);
  13672             const rhs_elems = try sema.arena.alloc(InternPool.Index, rhs_len);
  13673             for (rhs_elems, 0..) |*rhs_elem, i| rhs_elem.* = (try pt.intValue(
  13674                 rt_rhs_scalar_ty,
  13675                 @min(try (try rhs_val.elemValue(pt, i)).getUnsignedIntSema(pt) orelse bit_count, bit_count),
  13676             )).toIntern();
  13677             break :rt_rhs try pt.intern(.{ .aggregate = .{
  13678                 .ty = (try pt.vectorType(.{
  13679                     .len = rhs_len,
  13680                     .child = rt_rhs_scalar_ty.toIntern(),
  13681                 })).toIntern(),
  13682                 .storage = .{ .elems = rhs_elems },
  13683             } });
  13684         }) else rhs,
  13685     };
  13686 
  13687     try sema.requireRuntimeBlock(block, src, runtime_src);
  13688     if (block.wantSafety()) {
  13689         const bit_count = scalar_ty.intInfo(zcu).bits;
  13690         if (air_tag != .shl_sat and !std.math.isPowerOfTwo(bit_count)) {
  13691             const bit_count_val = try pt.intValue(scalar_rhs_ty, bit_count);
  13692             const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
  13693                 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern());
  13694                 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
  13695                 break :ok try block.addReduce(lt, .And);
  13696             } else ok: {
  13697                 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern());
  13698                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
  13699             };
  13700             try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
  13701         }
  13702 
  13703         if (air_tag == .shl_exact) {
  13704             const op_ov_tuple_ty = try pt.overflowArithmeticTupleType(lhs_ty);
  13705             const op_ov = try block.addInst(.{
  13706                 .tag = .shl_with_overflow,
  13707                 .data = .{ .ty_pl = .{
  13708                     .ty = Air.internedToRef(op_ov_tuple_ty.toIntern()),
  13709                     .payload = try sema.addExtra(Air.Bin{
  13710                         .lhs = lhs,
  13711                         .rhs = rhs,
  13712                     }),
  13713                 } },
  13714             });
  13715             const ov_bit = try sema.tupleFieldValByIndex(block, op_ov, 1, op_ov_tuple_ty);
  13716             const any_ov_bit = if (lhs_ty.zigTypeTag(zcu) == .vector)
  13717                 try block.addReduce(ov_bit, .Or)
  13718             else
  13719                 ov_bit;
  13720             const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, .zero_u1);
  13721 
  13722             try sema.addSafetyCheck(block, src, no_ov, .shl_overflow);
  13723             return sema.tupleFieldValByIndex(block, op_ov, 0, op_ov_tuple_ty);
  13724         }
  13725     }
  13726     return block.addBinOp(air_tag, lhs, rt_rhs);
  13727 }
  13728 
  13729 fn zirShr(
  13730     sema: *Sema,
  13731     block: *Block,
  13732     inst: Zir.Inst.Index,
  13733     air_tag: Air.Inst.Tag,
  13734 ) CompileError!Air.Inst.Ref {
  13735     const tracy = trace(@src());
  13736     defer tracy.end();
  13737 
  13738     const pt = sema.pt;
  13739     const zcu = pt.zcu;
  13740     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13741     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13742     const lhs = try sema.resolveInst(extra.lhs);
  13743     const rhs = try sema.resolveInst(extra.rhs);
  13744     const lhs_ty = sema.typeOf(lhs);
  13745     const rhs_ty = sema.typeOf(rhs);
  13746 
  13747     const src = block.nodeOffset(inst_data.src_node);
  13748     const lhs_src = switch (air_tag) {
  13749         .shr => block.src(.{ .node_offset_bin_lhs = inst_data.src_node }),
  13750         .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 0),
  13751         else => unreachable,
  13752     };
  13753     const rhs_src = switch (air_tag) {
  13754         .shr => block.src(.{ .node_offset_bin_rhs = inst_data.src_node }),
  13755         .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 1),
  13756         else => unreachable,
  13757     };
  13758 
  13759     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13760     const scalar_ty = lhs_ty.scalarType(zcu);
  13761 
  13762     const maybe_lhs_val = try sema.resolveValueResolveLazy(lhs);
  13763     const maybe_rhs_val = try sema.resolveValueResolveLazy(rhs);
  13764 
  13765     const runtime_src = if (maybe_rhs_val) |rhs_val| rs: {
  13766         if (rhs_val.isUndef(zcu)) {
  13767             return pt.undefRef(lhs_ty);
  13768         }
  13769         // If rhs is 0, return lhs without doing any calculations.
  13770         if (try rhs_val.compareAllWithZeroSema(.eq, pt)) {
  13771             return lhs;
  13772         }
  13773         if (scalar_ty.zigTypeTag(zcu) != .comptime_int) {
  13774             const bit_value = try pt.intValue(.comptime_int, scalar_ty.intInfo(zcu).bits);
  13775             if (rhs_ty.zigTypeTag(zcu) == .vector) {
  13776                 var i: usize = 0;
  13777                 while (i < rhs_ty.vectorLen(zcu)) : (i += 1) {
  13778                     const rhs_elem = try rhs_val.elemValue(pt, i);
  13779                     if (rhs_elem.compareHetero(.gte, bit_value, zcu)) {
  13780                         return sema.fail(block, rhs_src, "shift amount '{f}' at index '{d}' is too large for operand type '{f}'", .{
  13781                             rhs_elem.fmtValueSema(pt, sema),
  13782                             i,
  13783                             scalar_ty.fmt(pt),
  13784                         });
  13785                     }
  13786                 }
  13787             } else if (rhs_val.compareHetero(.gte, bit_value, zcu)) {
  13788                 return sema.fail(block, rhs_src, "shift amount '{f}' is too large for operand type '{f}'", .{
  13789                     rhs_val.fmtValueSema(pt, sema),
  13790                     scalar_ty.fmt(pt),
  13791                 });
  13792             }
  13793         }
  13794         if (rhs_ty.zigTypeTag(zcu) == .vector) {
  13795             var i: usize = 0;
  13796             while (i < rhs_ty.vectorLen(zcu)) : (i += 1) {
  13797                 const rhs_elem = try rhs_val.elemValue(pt, i);
  13798                 if (rhs_elem.compareHetero(.lt, try pt.intValue(rhs_ty.childType(zcu), 0), zcu)) {
  13799                     return sema.fail(block, rhs_src, "shift by negative amount '{f}' at index '{d}'", .{
  13800                         rhs_elem.fmtValueSema(pt, sema),
  13801                         i,
  13802                     });
  13803                 }
  13804             }
  13805         } else if (rhs_val.compareHetero(.lt, try pt.intValue(rhs_ty, 0), zcu)) {
  13806             return sema.fail(block, rhs_src, "shift by negative amount '{f}'", .{
  13807                 rhs_val.fmtValueSema(pt, sema),
  13808             });
  13809         }
  13810         if (maybe_lhs_val) |lhs_val| {
  13811             if (lhs_val.isUndef(zcu)) {
  13812                 return pt.undefRef(lhs_ty);
  13813             }
  13814             if (air_tag == .shr_exact) {
  13815                 // Detect if any ones would be shifted out.
  13816                 const truncated = try lhs_val.intTruncBitsAsValue(lhs_ty, sema.arena, .unsigned, rhs_val, pt);
  13817                 if (!(try truncated.compareAllWithZeroSema(.eq, pt))) {
  13818                     return sema.fail(block, src, "exact shift shifted out 1 bits", .{});
  13819                 }
  13820             }
  13821             const val = try lhs_val.shr(rhs_val, lhs_ty, sema.arena, pt);
  13822             return Air.internedToRef(val.toIntern());
  13823         } else {
  13824             break :rs lhs_src;
  13825         }
  13826     } else rhs_src;
  13827 
  13828     if (maybe_rhs_val == null and scalar_ty.zigTypeTag(zcu) == .comptime_int) {
  13829         return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
  13830     }
  13831 
  13832     try sema.requireRuntimeBlock(block, src, runtime_src);
  13833     const result = try block.addBinOp(air_tag, lhs, rhs);
  13834     if (block.wantSafety()) {
  13835         const bit_count = scalar_ty.intInfo(zcu).bits;
  13836         if (!std.math.isPowerOfTwo(bit_count)) {
  13837             const bit_count_val = try pt.intValue(rhs_ty.scalarType(zcu), bit_count);
  13838 
  13839             const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
  13840                 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern());
  13841                 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
  13842                 break :ok try block.addReduce(lt, .And);
  13843             } else ok: {
  13844                 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern());
  13845                 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
  13846             };
  13847             try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
  13848         }
  13849 
  13850         if (air_tag == .shr_exact) {
  13851             const back = try block.addBinOp(.shl, result, rhs);
  13852 
  13853             const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
  13854                 const eql = try block.addCmpVector(lhs, back, .eq);
  13855                 break :ok try block.addReduce(eql, .And);
  13856             } else try block.addBinOp(.cmp_eq, lhs, back);
  13857             try sema.addSafetyCheck(block, src, ok, .shr_overflow);
  13858         }
  13859     }
  13860     return result;
  13861 }
  13862 
  13863 fn zirBitwise(
  13864     sema: *Sema,
  13865     block: *Block,
  13866     inst: Zir.Inst.Index,
  13867     air_tag: Air.Inst.Tag,
  13868 ) CompileError!Air.Inst.Ref {
  13869     const tracy = trace(@src());
  13870     defer tracy.end();
  13871 
  13872     const pt = sema.pt;
  13873     const zcu = pt.zcu;
  13874     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  13875     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  13876     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  13877     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  13878     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  13879     const lhs = try sema.resolveInst(extra.lhs);
  13880     const rhs = try sema.resolveInst(extra.rhs);
  13881     const lhs_ty = sema.typeOf(lhs);
  13882     const rhs_ty = sema.typeOf(rhs);
  13883     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  13884 
  13885     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  13886     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
  13887     const scalar_type = resolved_type.scalarType(zcu);
  13888     const scalar_tag = scalar_type.zigTypeTag(zcu);
  13889 
  13890     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  13891     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  13892 
  13893     const is_int_or_bool = scalar_tag == .int or scalar_tag == .comptime_int or scalar_tag == .bool;
  13894 
  13895     if (!is_int_or_bool) {
  13896         return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(zcu)), @tagName(rhs_ty.zigTypeTag(zcu)) });
  13897     }
  13898 
  13899     const runtime_src = runtime: {
  13900         // TODO: ask the linker what kind of relocations are available, and
  13901         // in some cases emit a Value that means "this decl's address AND'd with this operand".
  13902         if (try sema.resolveValueResolveLazy(casted_lhs)) |lhs_val| {
  13903             if (try sema.resolveValueResolveLazy(casted_rhs)) |rhs_val| {
  13904                 const result_val = switch (air_tag) {
  13905                     .bit_and => try lhs_val.bitwiseAnd(rhs_val, resolved_type, sema.arena, pt),
  13906                     .bit_or => try lhs_val.bitwiseOr(rhs_val, resolved_type, sema.arena, pt),
  13907                     .xor => try lhs_val.bitwiseXor(rhs_val, resolved_type, sema.arena, pt),
  13908                     else => unreachable,
  13909                 };
  13910                 return Air.internedToRef(result_val.toIntern());
  13911             } else {
  13912                 break :runtime rhs_src;
  13913             }
  13914         } else {
  13915             break :runtime lhs_src;
  13916         }
  13917     };
  13918 
  13919     try sema.requireRuntimeBlock(block, src, runtime_src);
  13920     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  13921 }
  13922 
  13923 fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  13924     const pt = sema.pt;
  13925     const zcu = pt.zcu;
  13926     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  13927     const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
  13928     const src = block.nodeOffset(inst_data.src_node);
  13929     const operand = try sema.resolveInst(inst_data.operand);
  13930     const operand_ty = sema.typeOf(operand);
  13931     const scalar_ty = operand_ty.scalarType(zcu);
  13932     const scalar_tag = scalar_ty.zigTypeTag(zcu);
  13933 
  13934     if (scalar_tag != .int and scalar_tag != .bool)
  13935         return sema.fail(block, operand_src, "bitwise not operation on type '{f}'", .{operand_ty.fmt(pt)});
  13936 
  13937     return analyzeBitNot(sema, block, operand, src);
  13938 }
  13939 
  13940 fn analyzeBitNot(
  13941     sema: *Sema,
  13942     block: *Block,
  13943     operand: Air.Inst.Ref,
  13944     src: LazySrcLoc,
  13945 ) CompileError!Air.Inst.Ref {
  13946     const pt = sema.pt;
  13947     const zcu = pt.zcu;
  13948     const operand_ty = sema.typeOf(operand);
  13949     const scalar_ty = operand_ty.scalarType(zcu);
  13950     if (try sema.resolveValue(operand)) |val| {
  13951         if (val.isUndef(zcu)) {
  13952             return pt.undefRef(operand_ty);
  13953         } else if (operand_ty.zigTypeTag(zcu) == .vector) {
  13954             const vec_len = try sema.usizeCast(block, src, operand_ty.vectorLen(zcu));
  13955             const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  13956             for (elems, 0..) |*elem, i| {
  13957                 const elem_val = try val.elemValue(pt, i);
  13958                 elem.* = (try elem_val.bitwiseNot(scalar_ty, sema.arena, pt)).toIntern();
  13959             }
  13960             return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  13961                 .ty = operand_ty.toIntern(),
  13962                 .storage = .{ .elems = elems },
  13963             } })));
  13964         } else {
  13965             const result_val = try val.bitwiseNot(operand_ty, sema.arena, pt);
  13966             return Air.internedToRef(result_val.toIntern());
  13967         }
  13968     }
  13969 
  13970     try sema.requireRuntimeBlock(block, src, null);
  13971     return block.addTyOp(.not, operand_ty, operand);
  13972 }
  13973 
  13974 fn analyzeTupleCat(
  13975     sema: *Sema,
  13976     block: *Block,
  13977     src_node: std.zig.Ast.Node.Offset,
  13978     lhs: Air.Inst.Ref,
  13979     rhs: Air.Inst.Ref,
  13980 ) CompileError!Air.Inst.Ref {
  13981     const pt = sema.pt;
  13982     const zcu = pt.zcu;
  13983     const lhs_ty = sema.typeOf(lhs);
  13984     const rhs_ty = sema.typeOf(rhs);
  13985     const src = block.nodeOffset(src_node);
  13986 
  13987     const lhs_len = lhs_ty.structFieldCount(zcu);
  13988     const rhs_len = rhs_ty.structFieldCount(zcu);
  13989     const dest_fields = lhs_len + rhs_len;
  13990 
  13991     if (dest_fields == 0) {
  13992         return .empty_tuple;
  13993     }
  13994     if (lhs_len == 0) {
  13995         return rhs;
  13996     }
  13997     if (rhs_len == 0) {
  13998         return lhs;
  13999     }
  14000     const final_len = try sema.usizeCast(block, src, dest_fields);
  14001 
  14002     const types = try sema.arena.alloc(InternPool.Index, final_len);
  14003     const values = try sema.arena.alloc(InternPool.Index, final_len);
  14004 
  14005     const opt_runtime_src = rs: {
  14006         var runtime_src: ?LazySrcLoc = null;
  14007         var i: u32 = 0;
  14008         while (i < lhs_len) : (i += 1) {
  14009             types[i] = lhs_ty.fieldType(i, zcu).toIntern();
  14010             const default_val = lhs_ty.structFieldDefaultValue(i, zcu);
  14011             values[i] = default_val.toIntern();
  14012             const operand_src = block.src(.{ .array_cat_lhs = .{
  14013                 .array_cat_offset = src_node,
  14014                 .elem_index = i,
  14015             } });
  14016             if (default_val.toIntern() == .unreachable_value) {
  14017                 runtime_src = operand_src;
  14018                 values[i] = .none;
  14019             }
  14020         }
  14021         i = 0;
  14022         while (i < rhs_len) : (i += 1) {
  14023             types[i + lhs_len] = rhs_ty.fieldType(i, zcu).toIntern();
  14024             const default_val = rhs_ty.structFieldDefaultValue(i, zcu);
  14025             values[i + lhs_len] = default_val.toIntern();
  14026             const operand_src = block.src(.{ .array_cat_rhs = .{
  14027                 .array_cat_offset = src_node,
  14028                 .elem_index = i,
  14029             } });
  14030             if (default_val.toIntern() == .unreachable_value) {
  14031                 runtime_src = operand_src;
  14032                 values[i + lhs_len] = .none;
  14033             }
  14034         }
  14035         break :rs runtime_src;
  14036     };
  14037 
  14038     const tuple_ty = try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{
  14039         .types = types,
  14040         .values = values,
  14041     });
  14042 
  14043     const runtime_src = opt_runtime_src orelse {
  14044         const tuple_val = try pt.intern(.{ .aggregate = .{
  14045             .ty = tuple_ty,
  14046             .storage = .{ .elems = values },
  14047         } });
  14048         return Air.internedToRef(tuple_val);
  14049     };
  14050 
  14051     try sema.requireRuntimeBlock(block, src, runtime_src);
  14052 
  14053     const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
  14054     var i: u32 = 0;
  14055     while (i < lhs_len) : (i += 1) {
  14056         element_refs[i] = try sema.tupleFieldValByIndex(block, lhs, i, lhs_ty);
  14057     }
  14058     i = 0;
  14059     while (i < rhs_len) : (i += 1) {
  14060         element_refs[i + lhs_len] =
  14061             try sema.tupleFieldValByIndex(block, rhs, i, rhs_ty);
  14062     }
  14063 
  14064     return block.addAggregateInit(.fromInterned(tuple_ty), element_refs);
  14065 }
  14066 
  14067 fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14068     const tracy = trace(@src());
  14069     defer tracy.end();
  14070 
  14071     const pt = sema.pt;
  14072     const zcu = pt.zcu;
  14073     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14074     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14075     const lhs = try sema.resolveInst(extra.lhs);
  14076     const rhs = try sema.resolveInst(extra.rhs);
  14077     const lhs_ty = sema.typeOf(lhs);
  14078     const rhs_ty = sema.typeOf(rhs);
  14079     const src = block.nodeOffset(inst_data.src_node);
  14080 
  14081     const lhs_is_tuple = lhs_ty.isTuple(zcu);
  14082     const rhs_is_tuple = rhs_ty.isTuple(zcu);
  14083     if (lhs_is_tuple and rhs_is_tuple) {
  14084         return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs);
  14085     }
  14086 
  14087     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  14088     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  14089 
  14090     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: {
  14091         if (lhs_is_tuple) break :lhs_info undefined;
  14092         return sema.fail(block, lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)});
  14093     };
  14094     const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse {
  14095         assert(!rhs_is_tuple);
  14096         return sema.fail(block, rhs_src, "expected indexable; found '{f}'", .{rhs_ty.fmt(pt)});
  14097     };
  14098 
  14099     const resolved_elem_ty = t: {
  14100         var trash_block = block.makeSubBlock();
  14101         trash_block.comptime_reason = null;
  14102         defer trash_block.instructions.deinit(sema.gpa);
  14103 
  14104         const instructions = [_]Air.Inst.Ref{
  14105             try trash_block.addBitCast(lhs_info.elem_type, .void_value),
  14106             try trash_block.addBitCast(rhs_info.elem_type, .void_value),
  14107         };
  14108         break :t try sema.resolvePeerTypes(block, src, &instructions, .{
  14109             .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  14110         });
  14111     };
  14112 
  14113     // When there is a sentinel mismatch, no sentinel on the result.
  14114     // Otherwise, use the sentinel value provided by either operand,
  14115     // coercing it to the peer-resolved element type.
  14116     const res_sent_val: ?Value = s: {
  14117         if (lhs_info.sentinel) |lhs_sent_val| {
  14118             const lhs_sent = Air.internedToRef(lhs_sent_val.toIntern());
  14119             if (rhs_info.sentinel) |rhs_sent_val| {
  14120                 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern());
  14121                 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
  14122                 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
  14123                 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?;
  14124                 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?;
  14125                 if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) {
  14126                     break :s lhs_sent_casted_val;
  14127                 } else {
  14128                     break :s null;
  14129                 }
  14130             } else {
  14131                 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
  14132                 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?;
  14133                 break :s lhs_sent_casted_val;
  14134             }
  14135         } else {
  14136             if (rhs_info.sentinel) |rhs_sent_val| {
  14137                 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern());
  14138                 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
  14139                 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?;
  14140                 break :s rhs_sent_casted_val;
  14141             } else {
  14142                 break :s null;
  14143             }
  14144         }
  14145     };
  14146 
  14147     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
  14148     const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len);
  14149     const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) {
  14150         error.Overflow => return sema.fail(
  14151             block,
  14152             src,
  14153             "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
  14154             .{ lhs_len, rhs_len },
  14155         ),
  14156     };
  14157 
  14158     const result_ty = try pt.arrayType(.{
  14159         .len = result_len,
  14160         .sentinel = if (res_sent_val) |v| v.toIntern() else .none,
  14161         .child = resolved_elem_ty.toIntern(),
  14162     });
  14163     const ptr_addrspace = p: {
  14164         if (lhs_ty.zigTypeTag(zcu) == .pointer) break :p lhs_ty.ptrAddressSpace(zcu);
  14165         if (rhs_ty.zigTypeTag(zcu) == .pointer) break :p rhs_ty.ptrAddressSpace(zcu);
  14166         break :p null;
  14167     };
  14168 
  14169     const runtime_src = if (switch (lhs_ty.zigTypeTag(zcu)) {
  14170         .array, .@"struct" => try sema.resolveValue(lhs),
  14171         .pointer => try sema.resolveDefinedValue(block, lhs_src, lhs),
  14172         else => unreachable,
  14173     }) |lhs_val| rs: {
  14174         if (switch (rhs_ty.zigTypeTag(zcu)) {
  14175             .array, .@"struct" => try sema.resolveValue(rhs),
  14176             .pointer => try sema.resolveDefinedValue(block, rhs_src, rhs),
  14177             else => unreachable,
  14178         }) |rhs_val| {
  14179             const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu))
  14180                 try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :rs lhs_src
  14181             else if (lhs_ty.isSlice(zcu))
  14182                 try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :rs lhs_src
  14183             else
  14184                 lhs_val;
  14185 
  14186             const rhs_sub_val = if (rhs_ty.isSinglePointer(zcu))
  14187                 try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty) orelse break :rs rhs_src
  14188             else if (rhs_ty.isSlice(zcu))
  14189                 try sema.maybeDerefSliceAsArray(block, rhs_src, rhs_val) orelse break :rs rhs_src
  14190             else
  14191                 rhs_val;
  14192 
  14193             const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
  14194             var elem_i: u32 = 0;
  14195             while (elem_i < lhs_len) : (elem_i += 1) {
  14196                 const lhs_elem_i = elem_i;
  14197                 const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else Value.@"unreachable";
  14198                 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(pt, lhs_elem_i) else elem_default_val;
  14199                 const elem_val_inst = Air.internedToRef(elem_val.toIntern());
  14200                 const operand_src = block.src(.{ .array_cat_lhs = .{
  14201                     .array_cat_offset = inst_data.src_node,
  14202                     .elem_index = elem_i,
  14203                 } });
  14204                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src);
  14205                 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined);
  14206                 element_vals[elem_i] = coerced_elem_val.toIntern();
  14207             }
  14208             while (elem_i < result_len) : (elem_i += 1) {
  14209                 const rhs_elem_i = elem_i - lhs_len;
  14210                 const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else Value.@"unreachable";
  14211                 const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(pt, rhs_elem_i) else elem_default_val;
  14212                 const elem_val_inst = Air.internedToRef(elem_val.toIntern());
  14213                 const operand_src = block.src(.{ .array_cat_rhs = .{
  14214                     .array_cat_offset = inst_data.src_node,
  14215                     .elem_index = @intCast(rhs_elem_i),
  14216                 } });
  14217                 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, operand_src);
  14218                 const coerced_elem_val = try sema.resolveConstValue(block, operand_src, coerced_elem_val_inst, undefined);
  14219                 element_vals[elem_i] = coerced_elem_val.toIntern();
  14220             }
  14221             return sema.addConstantMaybeRef(try pt.intern(.{ .aggregate = .{
  14222                 .ty = result_ty.toIntern(),
  14223                 .storage = .{ .elems = element_vals },
  14224             } }), ptr_addrspace != null);
  14225         } else break :rs rhs_src;
  14226     } else lhs_src;
  14227 
  14228     try sema.requireRuntimeBlock(block, src, runtime_src);
  14229 
  14230     if (ptr_addrspace) |ptr_as| {
  14231         const constant_alloc_ty = try pt.ptrTypeSema(.{
  14232             .child = result_ty.toIntern(),
  14233             .flags = .{
  14234                 .address_space = ptr_as,
  14235                 .is_const = true,
  14236             },
  14237         });
  14238         const alloc_ty = try pt.ptrTypeSema(.{
  14239             .child = result_ty.toIntern(),
  14240             .flags = .{ .address_space = ptr_as },
  14241         });
  14242         const elem_ptr_ty = try pt.ptrTypeSema(.{
  14243             .child = resolved_elem_ty.toIntern(),
  14244             .flags = .{ .address_space = ptr_as },
  14245         });
  14246 
  14247         const mutable_alloc = try block.addTy(.alloc, alloc_ty);
  14248 
  14249         // if both the source and destination are arrays
  14250         // we can hotpath via a memcpy.
  14251         if (lhs_ty.zigTypeTag(zcu) == .pointer and
  14252             rhs_ty.zigTypeTag(zcu) == .pointer)
  14253         {
  14254             const slice_ty = try pt.ptrTypeSema(.{
  14255                 .child = resolved_elem_ty.toIntern(),
  14256                 .flags = .{
  14257                     .size = .slice,
  14258                     .address_space = ptr_as,
  14259                 },
  14260             });
  14261 
  14262             const many_ty = slice_ty.slicePtrFieldType(zcu);
  14263             const many_alloc = try block.addBitCast(many_ty, mutable_alloc);
  14264 
  14265             // lhs_dest_slice = dest[0..lhs.len]
  14266             const slice_ty_ref = Air.internedToRef(slice_ty.toIntern());
  14267             const lhs_len_ref = try pt.intRef(.usize, lhs_len);
  14268             const lhs_dest_slice = try block.addInst(.{
  14269                 .tag = .slice,
  14270                 .data = .{ .ty_pl = .{
  14271                     .ty = slice_ty_ref,
  14272                     .payload = try sema.addExtra(Air.Bin{
  14273                         .lhs = many_alloc,
  14274                         .rhs = lhs_len_ref,
  14275                     }),
  14276                 } },
  14277             });
  14278 
  14279             _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs);
  14280 
  14281             // rhs_dest_slice = dest[lhs.len..][0..rhs.len]
  14282             const rhs_len_ref = try pt.intRef(.usize, rhs_len);
  14283             const rhs_dest_offset = try block.addInst(.{
  14284                 .tag = .ptr_add,
  14285                 .data = .{ .ty_pl = .{
  14286                     .ty = Air.internedToRef(many_ty.toIntern()),
  14287                     .payload = try sema.addExtra(Air.Bin{
  14288                         .lhs = many_alloc,
  14289                         .rhs = lhs_len_ref,
  14290                     }),
  14291                 } },
  14292             });
  14293             const rhs_dest_slice = try block.addInst(.{
  14294                 .tag = .slice,
  14295                 .data = .{ .ty_pl = .{
  14296                     .ty = slice_ty_ref,
  14297                     .payload = try sema.addExtra(Air.Bin{
  14298                         .lhs = rhs_dest_offset,
  14299                         .rhs = rhs_len_ref,
  14300                     }),
  14301                 } },
  14302             });
  14303 
  14304             _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs);
  14305 
  14306             if (res_sent_val) |sent_val| {
  14307                 const elem_index = try pt.intRef(.usize, result_len);
  14308                 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
  14309                 const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern());
  14310                 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  14311             }
  14312 
  14313             return block.addBitCast(constant_alloc_ty, mutable_alloc);
  14314         }
  14315 
  14316         var elem_i: u32 = 0;
  14317         while (elem_i < lhs_len) : (elem_i += 1) {
  14318             const elem_index = try pt.intRef(.usize, elem_i);
  14319             const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
  14320             const operand_src = block.src(.{ .array_cat_lhs = .{
  14321                 .array_cat_offset = inst_data.src_node,
  14322                 .elem_index = elem_i,
  14323             } });
  14324             const init = try sema.elemVal(block, operand_src, lhs, elem_index, src, true);
  14325             try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store);
  14326         }
  14327         while (elem_i < result_len) : (elem_i += 1) {
  14328             const rhs_elem_i = elem_i - lhs_len;
  14329             const elem_index = try pt.intRef(.usize, elem_i);
  14330             const rhs_index = try pt.intRef(.usize, rhs_elem_i);
  14331             const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
  14332             const operand_src = block.src(.{ .array_cat_rhs = .{
  14333                 .array_cat_offset = inst_data.src_node,
  14334                 .elem_index = @intCast(rhs_elem_i),
  14335             } });
  14336             const init = try sema.elemVal(block, operand_src, rhs, rhs_index, src, true);
  14337             try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store);
  14338         }
  14339         if (res_sent_val) |sent_val| {
  14340             const elem_index = try pt.intRef(.usize, result_len);
  14341             const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
  14342             const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern());
  14343             try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  14344         }
  14345 
  14346         return block.addBitCast(constant_alloc_ty, mutable_alloc);
  14347     }
  14348 
  14349     const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
  14350     {
  14351         var elem_i: u32 = 0;
  14352         while (elem_i < lhs_len) : (elem_i += 1) {
  14353             const index = try pt.intRef(.usize, elem_i);
  14354             const operand_src = block.src(.{ .array_cat_lhs = .{
  14355                 .array_cat_offset = inst_data.src_node,
  14356                 .elem_index = elem_i,
  14357             } });
  14358             const init = try sema.elemVal(block, operand_src, lhs, index, src, true);
  14359             element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src);
  14360         }
  14361         while (elem_i < result_len) : (elem_i += 1) {
  14362             const rhs_elem_i = elem_i - lhs_len;
  14363             const index = try pt.intRef(.usize, rhs_elem_i);
  14364             const operand_src = block.src(.{ .array_cat_rhs = .{
  14365                 .array_cat_offset = inst_data.src_node,
  14366                 .elem_index = @intCast(rhs_elem_i),
  14367             } });
  14368             const init = try sema.elemVal(block, operand_src, rhs, index, src, true);
  14369             element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src);
  14370         }
  14371     }
  14372 
  14373     return block.addAggregateInit(result_ty, element_refs);
  14374 }
  14375 
  14376 fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo {
  14377     const pt = sema.pt;
  14378     const zcu = pt.zcu;
  14379     const operand_ty = sema.typeOf(operand);
  14380     switch (operand_ty.zigTypeTag(zcu)) {
  14381         .array => return operand_ty.arrayInfo(zcu),
  14382         .pointer => {
  14383             const ptr_info = operand_ty.ptrInfo(zcu);
  14384             switch (ptr_info.flags.size) {
  14385                 .slice => {
  14386                     const val = try sema.resolveConstDefinedValue(block, src, operand, .{ .simple = .slice_cat_operand });
  14387                     return .{
  14388                         .elem_type = .fromInterned(ptr_info.child),
  14389                         .sentinel = switch (ptr_info.sentinel) {
  14390                             .none => null,
  14391                             else => Value.fromInterned(ptr_info.sentinel),
  14392                         },
  14393                         .len = try val.sliceLen(pt),
  14394                     };
  14395                 },
  14396                 .one => {
  14397                     if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) {
  14398                         return Type.fromInterned(ptr_info.child).arrayInfo(zcu);
  14399                     }
  14400                 },
  14401                 .c, .many => {},
  14402             }
  14403         },
  14404         .@"struct" => {
  14405             if (operand_ty.isTuple(zcu) and peer_ty.isIndexable(zcu)) {
  14406                 assert(!peer_ty.isTuple(zcu));
  14407                 return .{
  14408                     .elem_type = peer_ty.elemType2(zcu),
  14409                     .sentinel = null,
  14410                     .len = operand_ty.arrayLen(zcu),
  14411                 };
  14412             }
  14413         },
  14414         else => {},
  14415     }
  14416     return null;
  14417 }
  14418 
  14419 fn analyzeTupleMul(
  14420     sema: *Sema,
  14421     block: *Block,
  14422     src_node: std.zig.Ast.Node.Offset,
  14423     operand: Air.Inst.Ref,
  14424     factor: usize,
  14425 ) CompileError!Air.Inst.Ref {
  14426     const pt = sema.pt;
  14427     const zcu = pt.zcu;
  14428     const operand_ty = sema.typeOf(operand);
  14429     const src = block.nodeOffset(src_node);
  14430     const len_src = block.src(.{ .node_offset_bin_rhs = src_node });
  14431 
  14432     const tuple_len = operand_ty.structFieldCount(zcu);
  14433     const final_len = std.math.mul(usize, tuple_len, factor) catch
  14434         return sema.fail(block, len_src, "operation results in overflow", .{});
  14435 
  14436     if (final_len == 0) {
  14437         return .empty_tuple;
  14438     }
  14439     const types = try sema.arena.alloc(InternPool.Index, final_len);
  14440     const values = try sema.arena.alloc(InternPool.Index, final_len);
  14441 
  14442     const opt_runtime_src = rs: {
  14443         var runtime_src: ?LazySrcLoc = null;
  14444         for (0..tuple_len) |i| {
  14445             types[i] = operand_ty.fieldType(i, zcu).toIntern();
  14446             values[i] = operand_ty.structFieldDefaultValue(i, zcu).toIntern();
  14447             const operand_src = block.src(.{ .array_cat_lhs = .{
  14448                 .array_cat_offset = src_node,
  14449                 .elem_index = @intCast(i),
  14450             } });
  14451             if (values[i] == .unreachable_value) {
  14452                 runtime_src = operand_src;
  14453                 values[i] = .none; // TODO don't treat unreachable_value as special
  14454             }
  14455         }
  14456         for (0..factor) |i| {
  14457             mem.copyForwards(InternPool.Index, types[tuple_len * i ..], types[0..tuple_len]);
  14458             mem.copyForwards(InternPool.Index, values[tuple_len * i ..], values[0..tuple_len]);
  14459         }
  14460         break :rs runtime_src;
  14461     };
  14462 
  14463     const tuple_ty = try zcu.intern_pool.getTupleType(zcu.gpa, pt.tid, .{
  14464         .types = types,
  14465         .values = values,
  14466     });
  14467 
  14468     const runtime_src = opt_runtime_src orelse {
  14469         const tuple_val = try pt.intern(.{ .aggregate = .{
  14470             .ty = tuple_ty,
  14471             .storage = .{ .elems = values },
  14472         } });
  14473         return Air.internedToRef(tuple_val);
  14474     };
  14475 
  14476     try sema.requireRuntimeBlock(block, src, runtime_src);
  14477 
  14478     const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
  14479     var i: u32 = 0;
  14480     while (i < tuple_len) : (i += 1) {
  14481         element_refs[i] = try sema.tupleFieldValByIndex(block, operand, @intCast(i), operand_ty);
  14482     }
  14483     i = 1;
  14484     while (i < factor) : (i += 1) {
  14485         @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]);
  14486     }
  14487 
  14488     return block.addAggregateInit(.fromInterned(tuple_ty), element_refs);
  14489 }
  14490 
  14491 fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14492     const tracy = trace(@src());
  14493     defer tracy.end();
  14494 
  14495     const pt = sema.pt;
  14496     const zcu = pt.zcu;
  14497     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14498     const extra = sema.code.extraData(Zir.Inst.ArrayMul, inst_data.payload_index).data;
  14499     const uncoerced_lhs = try sema.resolveInst(extra.lhs);
  14500     const uncoerced_lhs_ty = sema.typeOf(uncoerced_lhs);
  14501     const src: LazySrcLoc = block.nodeOffset(inst_data.src_node);
  14502     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  14503     const operator_src = block.src(.{ .node_offset_main_token = inst_data.src_node });
  14504     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  14505 
  14506     const lhs, const lhs_ty = coerced_lhs: {
  14507         // If we have a result type, we might be able to do this more efficiently
  14508         // by coercing the LHS first. Specifically, if we want an array or vector
  14509         // and have a tuple, coerce the tuple immediately.
  14510         no_coerce: {
  14511             if (extra.res_ty == .none) break :no_coerce;
  14512             const res_ty = try sema.resolveTypeOrPoison(block, src, extra.res_ty) orelse break :no_coerce;
  14513             if (!uncoerced_lhs_ty.isTuple(zcu)) break :no_coerce;
  14514             const lhs_len = uncoerced_lhs_ty.structFieldCount(zcu);
  14515             const lhs_dest_ty = switch (res_ty.zigTypeTag(zcu)) {
  14516                 else => break :no_coerce,
  14517                 .array => try pt.arrayType(.{
  14518                     .child = res_ty.childType(zcu).toIntern(),
  14519                     .len = lhs_len,
  14520                     .sentinel = if (res_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  14521                 }),
  14522                 .vector => try pt.vectorType(.{
  14523                     .child = res_ty.childType(zcu).toIntern(),
  14524                     .len = lhs_len,
  14525                 }),
  14526             };
  14527             // Attempt to coerce to this type, but don't emit an error if it fails. Instead,
  14528             // just exit out of this path and let the usual error happen later, so that error
  14529             // messages are consistent.
  14530             const coerced = sema.coerceExtra(block, lhs_dest_ty, uncoerced_lhs, lhs_src, .{ .report_err = false }) catch |err| switch (err) {
  14531                 error.NotCoercible => break :no_coerce,
  14532                 else => |e| return e,
  14533             };
  14534             break :coerced_lhs .{ coerced, lhs_dest_ty };
  14535         }
  14536         break :coerced_lhs .{ uncoerced_lhs, uncoerced_lhs_ty };
  14537     };
  14538 
  14539     if (lhs_ty.isTuple(zcu)) {
  14540         // In `**` rhs must be comptime-known, but lhs can be runtime-known
  14541         const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor });
  14542         const factor_casted = try sema.usizeCast(block, rhs_src, factor);
  14543         return sema.analyzeTupleMul(block, inst_data.src_node, lhs, factor_casted);
  14544     }
  14545 
  14546     // Analyze the lhs first, to catch the case that someone tried to do exponentiation
  14547     const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, lhs_ty) orelse {
  14548         const msg = msg: {
  14549             const msg = try sema.errMsg(lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)});
  14550             errdefer msg.destroy(sema.gpa);
  14551             switch (lhs_ty.zigTypeTag(zcu)) {
  14552                 .int, .float, .comptime_float, .comptime_int, .vector => {
  14553                     try sema.errNote(operator_src, msg, "this operator multiplies arrays; use std.math.pow for exponentiation", .{});
  14554                 },
  14555                 else => {},
  14556             }
  14557             break :msg msg;
  14558         };
  14559         return sema.failWithOwnedErrorMsg(block, msg);
  14560     };
  14561 
  14562     // In `**` rhs must be comptime-known, but lhs can be runtime-known
  14563     const factor = try sema.resolveInt(block, rhs_src, extra.rhs, .usize, .{ .simple = .array_mul_factor });
  14564 
  14565     const result_len_u64 = std.math.mul(u64, lhs_info.len, factor) catch
  14566         return sema.fail(block, rhs_src, "operation results in overflow", .{});
  14567     const result_len = try sema.usizeCast(block, src, result_len_u64);
  14568 
  14569     const result_ty = try pt.arrayType(.{
  14570         .len = result_len,
  14571         .sentinel = if (lhs_info.sentinel) |s| s.toIntern() else .none,
  14572         .child = lhs_info.elem_type.toIntern(),
  14573     });
  14574 
  14575     const ptr_addrspace = if (lhs_ty.zigTypeTag(zcu) == .pointer) lhs_ty.ptrAddressSpace(zcu) else null;
  14576     const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
  14577 
  14578     if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| ct: {
  14579         const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu))
  14580             try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :ct
  14581         else if (lhs_ty.isSlice(zcu))
  14582             try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :ct
  14583         else
  14584             lhs_val;
  14585 
  14586         const val = v: {
  14587             // Optimization for the common pattern of a single element repeated N times, such
  14588             // as zero-filling a byte array.
  14589             if (lhs_len == 1 and lhs_info.sentinel == null) {
  14590                 const elem_val = try lhs_sub_val.elemValue(pt, 0);
  14591                 break :v try pt.intern(.{ .aggregate = .{
  14592                     .ty = result_ty.toIntern(),
  14593                     .storage = .{ .repeated_elem = elem_val.toIntern() },
  14594                 } });
  14595             }
  14596 
  14597             const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
  14598             var elem_i: usize = 0;
  14599             while (elem_i < result_len) {
  14600                 var lhs_i: usize = 0;
  14601                 while (lhs_i < lhs_len) : (lhs_i += 1) {
  14602                     const elem_val = try lhs_sub_val.elemValue(pt, lhs_i);
  14603                     element_vals[elem_i] = elem_val.toIntern();
  14604                     elem_i += 1;
  14605                 }
  14606             }
  14607             break :v try pt.intern(.{ .aggregate = .{
  14608                 .ty = result_ty.toIntern(),
  14609                 .storage = .{ .elems = element_vals },
  14610             } });
  14611         };
  14612         return sema.addConstantMaybeRef(val, ptr_addrspace != null);
  14613     }
  14614 
  14615     try sema.requireRuntimeBlock(block, src, lhs_src);
  14616 
  14617     // Grab all the LHS values ahead of time, rather than repeatedly emitting instructions
  14618     // to get the same elem values.
  14619     const lhs_vals = try sema.arena.alloc(Air.Inst.Ref, lhs_len);
  14620     for (lhs_vals, 0..) |*lhs_val, idx| {
  14621         const idx_ref = try pt.intRef(.usize, idx);
  14622         lhs_val.* = try sema.elemVal(block, lhs_src, lhs, idx_ref, src, false);
  14623     }
  14624 
  14625     if (ptr_addrspace) |ptr_as| {
  14626         const alloc_ty = try pt.ptrTypeSema(.{
  14627             .child = result_ty.toIntern(),
  14628             .flags = .{
  14629                 .address_space = ptr_as,
  14630                 .is_const = true,
  14631             },
  14632         });
  14633         const alloc = try block.addTy(.alloc, alloc_ty);
  14634         const elem_ptr_ty = try pt.ptrTypeSema(.{
  14635             .child = lhs_info.elem_type.toIntern(),
  14636             .flags = .{ .address_space = ptr_as },
  14637         });
  14638 
  14639         var elem_i: usize = 0;
  14640         while (elem_i < result_len) {
  14641             for (lhs_vals) |lhs_val| {
  14642                 const elem_index = try pt.intRef(.usize, elem_i);
  14643                 const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  14644                 try sema.storePtr2(block, src, elem_ptr, src, lhs_val, lhs_src, .store);
  14645                 elem_i += 1;
  14646             }
  14647         }
  14648         if (lhs_info.sentinel) |sent_val| {
  14649             const elem_index = try pt.intRef(.usize, result_len);
  14650             const elem_ptr = try block.addPtrElemPtr(alloc, elem_index, elem_ptr_ty);
  14651             const init = Air.internedToRef(sent_val.toIntern());
  14652             try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
  14653         }
  14654 
  14655         return alloc;
  14656     }
  14657 
  14658     const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
  14659     for (0..try sema.usizeCast(block, rhs_src, factor)) |i| {
  14660         @memcpy(element_refs[i * lhs_len ..][0..lhs_len], lhs_vals);
  14661     }
  14662     return block.addAggregateInit(result_ty, element_refs);
  14663 }
  14664 
  14665 fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14666     const pt = sema.pt;
  14667     const zcu = pt.zcu;
  14668     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  14669     const src = block.nodeOffset(inst_data.src_node);
  14670     const lhs_src = src;
  14671     const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
  14672 
  14673     const rhs = try sema.resolveInst(inst_data.operand);
  14674     const rhs_ty = sema.typeOf(rhs);
  14675     const rhs_scalar_ty = rhs_ty.scalarType(zcu);
  14676 
  14677     if (rhs_scalar_ty.isUnsignedInt(zcu) or switch (rhs_scalar_ty.zigTypeTag(zcu)) {
  14678         .int, .comptime_int, .float, .comptime_float => false,
  14679         else => true,
  14680     }) {
  14681         return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)});
  14682     }
  14683 
  14684     if (rhs_scalar_ty.isAnyFloat()) {
  14685         // We handle float negation here to ensure negative zero is represented in the bits.
  14686         if (try sema.resolveValue(rhs)) |rhs_val| {
  14687             const result = try arith.negateFloat(sema, rhs_ty, rhs_val);
  14688             return Air.internedToRef(result.toIntern());
  14689         }
  14690         try sema.requireRuntimeBlock(block, src, null);
  14691         return block.addUnOp(if (block.float_mode == .optimized) .neg_optimized else .neg, rhs);
  14692     }
  14693 
  14694     const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern());
  14695     return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true);
  14696 }
  14697 
  14698 fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14699     const pt = sema.pt;
  14700     const zcu = pt.zcu;
  14701     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  14702     const src = block.nodeOffset(inst_data.src_node);
  14703     const lhs_src = src;
  14704     const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
  14705 
  14706     const rhs = try sema.resolveInst(inst_data.operand);
  14707     const rhs_ty = sema.typeOf(rhs);
  14708     const rhs_scalar_ty = rhs_ty.scalarType(zcu);
  14709 
  14710     switch (rhs_scalar_ty.zigTypeTag(zcu)) {
  14711         .int, .comptime_int, .float, .comptime_float => {},
  14712         else => return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)}),
  14713     }
  14714 
  14715     const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern());
  14716     return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true);
  14717 }
  14718 
  14719 fn zirArithmetic(
  14720     sema: *Sema,
  14721     block: *Block,
  14722     inst: Zir.Inst.Index,
  14723     zir_tag: Zir.Inst.Tag,
  14724     safety: bool,
  14725 ) CompileError!Air.Inst.Ref {
  14726     const tracy = trace(@src());
  14727     defer tracy.end();
  14728 
  14729     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14730     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  14731     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  14732     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  14733     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14734     const lhs = try sema.resolveInst(extra.lhs);
  14735     const rhs = try sema.resolveInst(extra.rhs);
  14736 
  14737     return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, src, lhs_src, rhs_src, safety);
  14738 }
  14739 
  14740 fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14741     const pt = sema.pt;
  14742     const zcu = pt.zcu;
  14743     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14744     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  14745     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  14746     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  14747     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14748     const lhs = try sema.resolveInst(extra.lhs);
  14749     const rhs = try sema.resolveInst(extra.rhs);
  14750     const lhs_ty = sema.typeOf(lhs);
  14751     const rhs_ty = sema.typeOf(rhs);
  14752     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  14753     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  14754     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14755     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  14756 
  14757     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  14758         .override = &.{ lhs_src, rhs_src },
  14759     });
  14760 
  14761     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14762     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14763 
  14764     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  14765     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  14766 
  14767     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  14768 
  14769     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div);
  14770 
  14771     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  14772     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  14773 
  14774     if ((lhs_ty.zigTypeTag(zcu) == .comptime_float and rhs_ty.zigTypeTag(zcu) == .comptime_int) or
  14775         (lhs_ty.zigTypeTag(zcu) == .comptime_int and rhs_ty.zigTypeTag(zcu) == .comptime_float))
  14776     {
  14777         // If it makes a difference whether we coerce to ints or floats before doing the division, error.
  14778         // If lhs % rhs is 0, it doesn't matter.
  14779         const lhs_val = maybe_lhs_val orelse unreachable;
  14780         const rhs_val = maybe_rhs_val orelse unreachable;
  14781         const rem = lhs_val.floatRem(rhs_val, resolved_type, sema.arena, pt) catch unreachable;
  14782         if (!rem.compareAllWithZero(.eq, zcu)) {
  14783             return sema.fail(
  14784                 block,
  14785                 src,
  14786                 "ambiguous coercion of division operands '{f}' and '{f}'; non-zero remainder '{f}'",
  14787                 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt), rem.fmtValueSema(pt, sema) },
  14788             );
  14789         }
  14790     }
  14791 
  14792     // TODO: emit compile error when .div is used on integers and there would be an
  14793     // ambiguous result between div_floor and div_trunc.
  14794 
  14795     // The rules here are like those in `analyzeArithmetic`:
  14796     //
  14797     // * If both operands are comptime-known, call the corresponding function in `arith`.
  14798     //   Inputs which would be IB at runtime are compile errors.
  14799     //
  14800     // * Otherwise, if one operand is comptime-known `undefined`, we either trigger a compile error
  14801     //   or return `undefined`, depending on whether this operator can trigger IB.
  14802     //
  14803     // * No other comptime operand determines a comptime result, so remaining cases are runtime ops.
  14804 
  14805     const allow_div_zero = !is_int and
  14806         resolved_type.toIntern() != .comptime_float_type and
  14807         block.float_mode == .strict;
  14808 
  14809     if (maybe_lhs_val) |lhs_val| {
  14810         if (maybe_rhs_val) |rhs_val| {
  14811             const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div);
  14812             return Air.internedToRef(result.toIntern());
  14813         }
  14814         if (allow_div_zero) {
  14815             if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  14816         } else {
  14817             if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src);
  14818         }
  14819     } else if (maybe_rhs_val) |rhs_val| {
  14820         if (allow_div_zero) {
  14821             if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  14822         } else {
  14823             if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src);
  14824             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  14825         }
  14826     }
  14827 
  14828     if (block.wantSafety()) {
  14829         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  14830         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14831     }
  14832 
  14833     const air_tag = if (is_int) blk: {
  14834         if (lhs_ty.isSignedInt(zcu) or rhs_ty.isSignedInt(zcu)) {
  14835             return sema.fail(
  14836                 block,
  14837                 src,
  14838                 "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, or @divExact",
  14839                 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) },
  14840             );
  14841         }
  14842         break :blk Air.Inst.Tag.div_trunc;
  14843     } else switch (block.float_mode) {
  14844         .optimized => Air.Inst.Tag.div_float_optimized,
  14845         .strict => Air.Inst.Tag.div_float,
  14846     };
  14847     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  14848 }
  14849 
  14850 fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14851     const pt = sema.pt;
  14852     const zcu = pt.zcu;
  14853     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14854     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  14855     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  14856     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  14857     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14858     const lhs = try sema.resolveInst(extra.lhs);
  14859     const rhs = try sema.resolveInst(extra.rhs);
  14860     const lhs_ty = sema.typeOf(lhs);
  14861     const rhs_ty = sema.typeOf(rhs);
  14862     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  14863     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  14864     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14865     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  14866 
  14867     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  14868         .override = &.{ lhs_src, rhs_src },
  14869     });
  14870 
  14871     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14872     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14873 
  14874     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  14875     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  14876 
  14877     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  14878 
  14879     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact);
  14880 
  14881     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  14882     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  14883 
  14884     // Because `@divExact` can trigger Illegal Behavior, undefined operands trigger Illegal Behavior.
  14885 
  14886     if (maybe_lhs_val) |lhs_val| {
  14887         if (maybe_rhs_val) |rhs_val| {
  14888             const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_exact);
  14889             return Air.internedToRef(result.toIntern());
  14890         }
  14891         if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src);
  14892     } else if (maybe_rhs_val) |rhs_val| {
  14893         if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src);
  14894         if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  14895     }
  14896 
  14897     // Depending on whether safety is enabled, we will have a slightly different strategy
  14898     // here. The `div_exact` AIR instruction causes illegal behavior if a remainder
  14899     // is produced, so in the safety check case, it cannot be used. Instead we do a
  14900     // div_trunc and check for remainder.
  14901 
  14902     if (block.wantSafety()) {
  14903         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  14904         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  14905 
  14906         const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs);
  14907         const ok = if (!is_int) ok: {
  14908             const floored = try block.addUnOp(.floor, result);
  14909 
  14910             if (resolved_type.zigTypeTag(zcu) == .vector) {
  14911                 const eql = try block.addCmpVector(result, floored, .eq);
  14912                 break :ok try block.addReduce(eql, .And);
  14913             } else {
  14914                 const is_in_range = try block.addBinOp(switch (block.float_mode) {
  14915                     .strict => .cmp_eq,
  14916                     .optimized => .cmp_eq_optimized,
  14917                 }, result, floored);
  14918                 break :ok is_in_range;
  14919             }
  14920         } else ok: {
  14921             const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
  14922 
  14923             const scalar_zero = switch (scalar_tag) {
  14924                 .comptime_float, .float => try pt.floatValue(resolved_type.scalarType(zcu), 0.0),
  14925                 .comptime_int, .int => try pt.intValue(resolved_type.scalarType(zcu), 0),
  14926                 else => unreachable,
  14927             };
  14928             if (resolved_type.zigTypeTag(zcu) == .vector) {
  14929                 const zero_val = try sema.splat(resolved_type, scalar_zero);
  14930                 const zero = Air.internedToRef(zero_val.toIntern());
  14931                 const eql = try block.addCmpVector(remainder, zero, .eq);
  14932                 break :ok try block.addReduce(eql, .And);
  14933             } else {
  14934                 const zero = Air.internedToRef(scalar_zero.toIntern());
  14935                 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero);
  14936                 break :ok is_in_range;
  14937             }
  14938         };
  14939         try sema.addSafetyCheck(block, src, ok, .exact_division_remainder);
  14940         return result;
  14941     }
  14942 
  14943     return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs);
  14944 }
  14945 
  14946 fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  14947     const pt = sema.pt;
  14948     const zcu = pt.zcu;
  14949     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  14950     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  14951     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  14952     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  14953     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  14954     const lhs = try sema.resolveInst(extra.lhs);
  14955     const rhs = try sema.resolveInst(extra.rhs);
  14956     const lhs_ty = sema.typeOf(lhs);
  14957     const rhs_ty = sema.typeOf(rhs);
  14958     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  14959     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  14960     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  14961     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  14962 
  14963     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  14964         .override = &.{ lhs_src, rhs_src },
  14965     });
  14966 
  14967     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  14968     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  14969 
  14970     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  14971     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  14972 
  14973     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  14974 
  14975     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor);
  14976 
  14977     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  14978     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  14979 
  14980     const allow_div_zero = !is_int and
  14981         resolved_type.toIntern() != .comptime_float_type and
  14982         block.float_mode == .strict;
  14983 
  14984     if (maybe_lhs_val) |lhs_val| {
  14985         if (maybe_rhs_val) |rhs_val| {
  14986             const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_floor);
  14987             return Air.internedToRef(result.toIntern());
  14988         }
  14989         if (allow_div_zero) {
  14990             if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  14991         } else {
  14992             if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src);
  14993         }
  14994     } else if (maybe_rhs_val) |rhs_val| {
  14995         if (allow_div_zero) {
  14996             if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  14997         } else {
  14998             if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src);
  14999             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15000         }
  15001     }
  15002 
  15003     if (block.wantSafety()) {
  15004         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  15005         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15006     }
  15007 
  15008     return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
  15009 }
  15010 
  15011 fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15012     const pt = sema.pt;
  15013     const zcu = pt.zcu;
  15014     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15015     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15016     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  15017     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  15018     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15019     const lhs = try sema.resolveInst(extra.lhs);
  15020     const rhs = try sema.resolveInst(extra.rhs);
  15021     const lhs_ty = sema.typeOf(lhs);
  15022     const rhs_ty = sema.typeOf(rhs);
  15023     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15024     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15025     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15026     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15027 
  15028     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15029         .override = &.{ lhs_src, rhs_src },
  15030     });
  15031 
  15032     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15033     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15034 
  15035     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  15036     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15037 
  15038     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15039 
  15040     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc);
  15041 
  15042     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15043     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15044 
  15045     const allow_div_zero = !is_int and
  15046         resolved_type.toIntern() != .comptime_float_type and
  15047         block.float_mode == .strict;
  15048 
  15049     if (maybe_lhs_val) |lhs_val| {
  15050         if (maybe_rhs_val) |rhs_val| {
  15051             const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_trunc);
  15052             return Air.internedToRef(result.toIntern());
  15053         }
  15054         if (allow_div_zero) {
  15055             if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15056         } else {
  15057             if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src);
  15058         }
  15059     } else if (maybe_rhs_val) |rhs_val| {
  15060         if (allow_div_zero) {
  15061             if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15062         } else {
  15063             if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src);
  15064             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15065         }
  15066     }
  15067 
  15068     if (block.wantSafety()) {
  15069         try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
  15070         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15071     }
  15072 
  15073     return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs);
  15074 }
  15075 
  15076 fn addDivIntOverflowSafety(
  15077     sema: *Sema,
  15078     block: *Block,
  15079     src: LazySrcLoc,
  15080     resolved_type: Type,
  15081     lhs_scalar_ty: Type,
  15082     maybe_lhs_val: ?Value,
  15083     maybe_rhs_val: ?Value,
  15084     casted_lhs: Air.Inst.Ref,
  15085     casted_rhs: Air.Inst.Ref,
  15086     is_int: bool,
  15087 ) CompileError!void {
  15088     const pt = sema.pt;
  15089     const zcu = pt.zcu;
  15090     if (!is_int) return;
  15091 
  15092     // If the LHS is unsigned, it cannot cause overflow.
  15093     if (!lhs_scalar_ty.isSignedInt(zcu)) return;
  15094 
  15095     // If the LHS is widened to a larger integer type, no overflow is possible.
  15096     if (lhs_scalar_ty.intInfo(zcu).bits < resolved_type.intInfo(zcu).bits) {
  15097         return;
  15098     }
  15099 
  15100     const min_int = try resolved_type.minInt(pt, resolved_type);
  15101     const neg_one_scalar = try pt.intValue(lhs_scalar_ty, -1);
  15102     const neg_one = try sema.splat(resolved_type, neg_one_scalar);
  15103 
  15104     // If the LHS is comptime-known to be not equal to the min int,
  15105     // no overflow is possible.
  15106     if (maybe_lhs_val) |lhs_val| {
  15107         if (try lhs_val.compareAll(.neq, min_int, resolved_type, pt)) return;
  15108     }
  15109 
  15110     // If the RHS is comptime-known to not be equal to -1, no overflow is possible.
  15111     if (maybe_rhs_val) |rhs_val| {
  15112         if (try rhs_val.compareAll(.neq, neg_one, resolved_type, pt)) return;
  15113     }
  15114 
  15115     if (resolved_type.zigTypeTag(zcu) == .vector) {
  15116         const vec_len = resolved_type.vectorLen(zcu);
  15117 
  15118         // This is a bool vector whose elements are true if the LHS element does NOT equal `min_int`.
  15119         const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val) |lhs_val| ok: {
  15120             // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements.
  15121             const min_int_scalar = try min_int.elemValue(pt, 0);
  15122             const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len);
  15123             for (elems_ok, 0..) |*elem_ok, elem_idx| {
  15124                 const elem_val = try lhs_val.elemValue(pt, elem_idx);
  15125                 elem_ok.* = if (elem_val.eqlScalarNum(min_int_scalar, zcu)) .bool_false else .bool_true;
  15126             }
  15127             break :ok Air.internedToRef(try pt.intern(.{ .aggregate = .{
  15128                 .ty = (try pt.vectorType(.{
  15129                     .len = vec_len,
  15130                     .child = .bool_type,
  15131                 })).toIntern(),
  15132                 .storage = .{ .elems = elems_ok },
  15133             } }));
  15134         } else ok: {
  15135             // The operand isn't comptime-known; add a runtime comparison.
  15136             const min_int_ref = Air.internedToRef(min_int.toIntern());
  15137             break :ok try block.addCmpVector(casted_lhs, min_int_ref, .neq);
  15138         };
  15139 
  15140         // This is a bool vector whose elements are true if the RHS element does NOT equal -1.
  15141         const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val) |rhs_val| ok: {
  15142             // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements.
  15143             const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len);
  15144             for (elems_ok, 0..) |*elem_ok, elem_idx| {
  15145                 const elem_val = try rhs_val.elemValue(pt, elem_idx);
  15146                 elem_ok.* = if (elem_val.eqlScalarNum(neg_one_scalar, zcu)) .bool_false else .bool_true;
  15147             }
  15148             break :ok Air.internedToRef(try pt.intern(.{ .aggregate = .{
  15149                 .ty = (try pt.vectorType(.{
  15150                     .len = vec_len,
  15151                     .child = .bool_type,
  15152                 })).toIntern(),
  15153                 .storage = .{ .elems = elems_ok },
  15154             } }));
  15155         } else ok: {
  15156             // The operand isn't comptime-known; add a runtime comparison.
  15157             const neg_one_ref = Air.internedToRef(neg_one.toIntern());
  15158             break :ok try block.addCmpVector(casted_rhs, neg_one_ref, .neq);
  15159         };
  15160 
  15161         const ok = try block.addReduce(try block.addBinOp(.bool_or, lhs_ok, rhs_ok), .And);
  15162         try sema.addSafetyCheck(block, src, ok, .integer_overflow);
  15163     } else {
  15164         const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val == null) ok: {
  15165             const min_int_ref = Air.internedToRef(min_int.toIntern());
  15166             break :ok try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref);
  15167         } else .none; // means false
  15168         const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val == null) ok: {
  15169             const neg_one_ref = Air.internedToRef(neg_one.toIntern());
  15170             break :ok try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref);
  15171         } else .none; // means false
  15172 
  15173         const ok = if (lhs_ok != .none and rhs_ok != .none)
  15174             try block.addBinOp(.bool_or, lhs_ok, rhs_ok)
  15175         else if (lhs_ok != .none)
  15176             lhs_ok
  15177         else if (rhs_ok != .none)
  15178             rhs_ok
  15179         else
  15180             unreachable;
  15181 
  15182         try sema.addSafetyCheck(block, src, ok, .integer_overflow);
  15183     }
  15184 }
  15185 
  15186 fn addDivByZeroSafety(
  15187     sema: *Sema,
  15188     block: *Block,
  15189     src: LazySrcLoc,
  15190     resolved_type: Type,
  15191     maybe_rhs_val: ?Value,
  15192     casted_rhs: Air.Inst.Ref,
  15193     is_int: bool,
  15194 ) CompileError!void {
  15195     // Strict IEEE floats have well-defined division by zero.
  15196     if (!is_int and block.float_mode == .strict) return;
  15197 
  15198     // If rhs was comptime-known to be zero a compile error would have been
  15199     // emitted above.
  15200     if (maybe_rhs_val != null) return;
  15201 
  15202     const pt = sema.pt;
  15203     const zcu = pt.zcu;
  15204     const scalar_zero = if (is_int)
  15205         try pt.intValue(resolved_type.scalarType(zcu), 0)
  15206     else
  15207         try pt.floatValue(resolved_type.scalarType(zcu), 0.0);
  15208     const ok = if (resolved_type.zigTypeTag(zcu) == .vector) ok: {
  15209         const zero_val = try sema.splat(resolved_type, scalar_zero);
  15210         const zero = Air.internedToRef(zero_val.toIntern());
  15211         const ok = try block.addCmpVector(casted_rhs, zero, .neq);
  15212         break :ok try block.addReduce(ok, .And);
  15213     } else ok: {
  15214         const zero = Air.internedToRef(scalar_zero.toIntern());
  15215         break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero);
  15216     };
  15217     try sema.addSafetyCheck(block, src, ok, .divide_by_zero);
  15218 }
  15219 
  15220 fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag {
  15221     if (is_int) return normal;
  15222     return switch (block.float_mode) {
  15223         .strict => normal,
  15224         .optimized => optimized,
  15225     };
  15226 }
  15227 
  15228 fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15229     const pt = sema.pt;
  15230     const zcu = pt.zcu;
  15231     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15232     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15233     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  15234     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  15235     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15236     const lhs = try sema.resolveInst(extra.lhs);
  15237     const rhs = try sema.resolveInst(extra.rhs);
  15238     const lhs_ty = sema.typeOf(lhs);
  15239     const rhs_ty = sema.typeOf(rhs);
  15240     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15241     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15242     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15243     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15244 
  15245     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15246         .override = &.{ lhs_src, rhs_src },
  15247     });
  15248 
  15249     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15250     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15251 
  15252     const lhs_scalar_ty = lhs_ty.scalarType(zcu);
  15253     const rhs_scalar_ty = rhs_ty.scalarType(zcu);
  15254     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15255 
  15256     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15257 
  15258     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem);
  15259 
  15260     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15261     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15262 
  15263     const lhs_maybe_negative = a: {
  15264         if (lhs_scalar_ty.isUnsignedInt(zcu)) break :a false;
  15265         const lhs_val = maybe_lhs_val orelse break :a true;
  15266         if (lhs_val.compareAllWithZero(.gte, zcu)) break :a false;
  15267         break :a true;
  15268     };
  15269     const rhs_maybe_negative = a: {
  15270         if (rhs_scalar_ty.isUnsignedInt(zcu)) break :a false;
  15271         const rhs_val = maybe_rhs_val orelse break :a true;
  15272         if (rhs_val.compareAllWithZero(.gte, zcu)) break :a false;
  15273         break :a true;
  15274     };
  15275 
  15276     if (maybe_lhs_val) |lhs_val| {
  15277         if (maybe_rhs_val) |rhs_val| {
  15278             const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem);
  15279             if (lhs_maybe_negative or rhs_maybe_negative) {
  15280                 if (!result.compareAllWithZero(.eq, zcu)) {
  15281                     // Non-zero result means ambiguity between mod and rem
  15282                     return sema.failWithModRemNegative(block, src: {
  15283                         if (lhs_maybe_negative) break :src lhs_src;
  15284                         if (rhs_maybe_negative) break :src rhs_src;
  15285                         unreachable;
  15286                     }, lhs_ty, rhs_ty);
  15287                 }
  15288             }
  15289             return Air.internedToRef(result.toIntern());
  15290         }
  15291     }
  15292 
  15293     // Result not comptime-known, so floats and signed integers are illegal due to mod/rem ambiguity
  15294     if (lhs_maybe_negative or rhs_maybe_negative) {
  15295         return sema.failWithModRemNegative(block, src: {
  15296             if (lhs_maybe_negative) break :src lhs_src;
  15297             if (rhs_maybe_negative) break :src rhs_src;
  15298             unreachable;
  15299         }, lhs_ty, rhs_ty);
  15300     }
  15301 
  15302     const allow_div_zero = !is_int and
  15303         resolved_type.toIntern() != .comptime_float_type and
  15304         block.float_mode == .strict;
  15305 
  15306     if (maybe_lhs_val) |lhs_val| {
  15307         if (allow_div_zero) {
  15308             if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15309         } else {
  15310             if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src);
  15311         }
  15312     } else if (maybe_rhs_val) |rhs_val| {
  15313         if (allow_div_zero) {
  15314             if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15315         } else {
  15316             if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src);
  15317             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15318         }
  15319     }
  15320 
  15321     if (block.wantSafety()) {
  15322         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15323     }
  15324 
  15325     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
  15326     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15327 }
  15328 
  15329 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15330     const pt = sema.pt;
  15331     const zcu = pt.zcu;
  15332     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15333     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15334     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  15335     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  15336     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15337     const lhs = try sema.resolveInst(extra.lhs);
  15338     const rhs = try sema.resolveInst(extra.rhs);
  15339     const lhs_ty = sema.typeOf(lhs);
  15340     const rhs_ty = sema.typeOf(rhs);
  15341     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15342     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15343     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15344     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15345 
  15346     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15347         .override = &.{ lhs_src, rhs_src },
  15348     });
  15349 
  15350     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15351     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15352 
  15353     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15354 
  15355     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15356 
  15357     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod);
  15358 
  15359     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15360     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15361 
  15362     const allow_div_zero = !is_int and
  15363         resolved_type.toIntern() != .comptime_float_type and
  15364         block.float_mode == .strict;
  15365 
  15366     if (maybe_lhs_val) |lhs_val| {
  15367         if (maybe_rhs_val) |rhs_val| {
  15368             const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .mod);
  15369             return Air.internedToRef(result.toIntern());
  15370         }
  15371         if (allow_div_zero) {
  15372             if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15373         } else {
  15374             if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src);
  15375         }
  15376     } else if (maybe_rhs_val) |rhs_val| {
  15377         if (allow_div_zero) {
  15378             if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15379         } else {
  15380             if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src);
  15381             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15382         }
  15383     }
  15384 
  15385     if (block.wantSafety()) {
  15386         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15387     }
  15388 
  15389     const air_tag = airTag(block, is_int, .mod, .mod_optimized);
  15390     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15391 }
  15392 
  15393 fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15394     const pt = sema.pt;
  15395     const zcu = pt.zcu;
  15396     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  15397     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  15398     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  15399     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  15400     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  15401     const lhs = try sema.resolveInst(extra.lhs);
  15402     const rhs = try sema.resolveInst(extra.rhs);
  15403     const lhs_ty = sema.typeOf(lhs);
  15404     const rhs_ty = sema.typeOf(rhs);
  15405     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15406     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15407     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15408     try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
  15409 
  15410     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15411         .override = &.{ lhs_src, rhs_src },
  15412     });
  15413 
  15414     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15415     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15416 
  15417     const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
  15418 
  15419     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  15420 
  15421     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem);
  15422 
  15423     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15424     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15425 
  15426     const allow_div_zero = !is_int and
  15427         resolved_type.toIntern() != .comptime_float_type and
  15428         block.float_mode == .strict;
  15429 
  15430     if (maybe_lhs_val) |lhs_val| {
  15431         if (maybe_rhs_val) |rhs_val| {
  15432             const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem);
  15433             return Air.internedToRef(result.toIntern());
  15434         }
  15435         if (allow_div_zero) {
  15436             if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15437         } else {
  15438             if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src);
  15439         }
  15440     } else if (maybe_rhs_val) |rhs_val| {
  15441         if (allow_div_zero) {
  15442             if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15443         } else {
  15444             if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src);
  15445             if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
  15446         }
  15447     }
  15448 
  15449     if (block.wantSafety()) {
  15450         try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
  15451     }
  15452 
  15453     const air_tag = airTag(block, is_int, .rem, .rem_optimized);
  15454     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15455 }
  15456 
  15457 fn zirOverflowArithmetic(
  15458     sema: *Sema,
  15459     block: *Block,
  15460     extended: Zir.Inst.Extended.InstData,
  15461     zir_tag: Zir.Inst.Extended,
  15462 ) CompileError!Air.Inst.Ref {
  15463     const tracy = trace(@src());
  15464     defer tracy.end();
  15465 
  15466     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  15467     const src = block.nodeOffset(extra.node);
  15468 
  15469     const lhs_src = block.builtinCallArgSrc(extra.node, 0);
  15470     const rhs_src = block.builtinCallArgSrc(extra.node, 1);
  15471 
  15472     const uncasted_lhs = try sema.resolveInst(extra.lhs);
  15473     const uncasted_rhs = try sema.resolveInst(extra.rhs);
  15474 
  15475     const lhs_ty = sema.typeOf(uncasted_lhs);
  15476     const rhs_ty = sema.typeOf(uncasted_rhs);
  15477     const pt = sema.pt;
  15478     const zcu = pt.zcu;
  15479     const ip = &zcu.intern_pool;
  15480 
  15481     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15482 
  15483     const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs };
  15484     const dest_ty = if (zir_tag == .shl_with_overflow)
  15485         lhs_ty
  15486     else
  15487         try sema.resolvePeerTypes(block, src, instructions, .{
  15488             .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  15489         });
  15490 
  15491     const rhs_dest_ty = if (zir_tag == .shl_with_overflow)
  15492         try sema.log2IntType(block, lhs_ty, src)
  15493     else
  15494         dest_ty;
  15495 
  15496     const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src);
  15497     const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src);
  15498 
  15499     if (dest_ty.scalarType(zcu).zigTypeTag(zcu) != .int) {
  15500         return sema.fail(block, src, "expected vector of integers or integer tag type, found '{f}'", .{dest_ty.fmt(pt)});
  15501     }
  15502 
  15503     const maybe_lhs_val = try sema.resolveValue(lhs);
  15504     const maybe_rhs_val = try sema.resolveValue(rhs);
  15505 
  15506     const tuple_ty = try pt.overflowArithmeticTupleType(dest_ty);
  15507     const overflow_ty: Type = .fromInterned(ip.indexToKey(tuple_ty.toIntern()).tuple_type.types.get(ip)[1]);
  15508 
  15509     var result: struct {
  15510         inst: Air.Inst.Ref = .none,
  15511         wrapped: Value = Value.@"unreachable",
  15512         overflow_bit: Value,
  15513     } = result: {
  15514         switch (zir_tag) {
  15515             .add_with_overflow => {
  15516                 // If either of the arguments is zero, `false` is returned and the other is stored
  15517                 // to the result, even if it is undefined..
  15518                 // Otherwise, if either of the argument is undefined, undefined is returned.
  15519                 if (maybe_lhs_val) |lhs_val| {
  15520                     if (!lhs_val.isUndef(zcu) and (try lhs_val.compareAllWithZeroSema(.eq, pt))) {
  15521                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
  15522                     }
  15523                 }
  15524                 if (maybe_rhs_val) |rhs_val| {
  15525                     if (!rhs_val.isUndef(zcu) and (try rhs_val.compareAllWithZeroSema(.eq, pt))) {
  15526                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15527                     }
  15528                 }
  15529                 if (maybe_lhs_val) |lhs_val| {
  15530                     if (maybe_rhs_val) |rhs_val| {
  15531                         if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
  15532                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15533                         }
  15534 
  15535                         const result = try arith.addWithOverflow(sema, dest_ty, lhs_val, rhs_val);
  15536                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15537                     }
  15538                 }
  15539             },
  15540             .sub_with_overflow => {
  15541                 // If the rhs is zero, then the result is lhs and no overflow occured.
  15542                 // Otherwise, if either result is undefined, both results are undefined.
  15543                 if (maybe_rhs_val) |rhs_val| {
  15544                     if (rhs_val.isUndef(zcu)) {
  15545                         break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15546                     } else if (try rhs_val.compareAllWithZeroSema(.eq, pt)) {
  15547                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15548                     } else if (maybe_lhs_val) |lhs_val| {
  15549                         if (lhs_val.isUndef(zcu)) {
  15550                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15551                         }
  15552 
  15553                         const result = try arith.subWithOverflow(sema, dest_ty, lhs_val, rhs_val);
  15554                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15555                     }
  15556                 }
  15557             },
  15558             .mul_with_overflow => {
  15559                 // If either of the arguments is zero, the result is zero and no overflow occured.
  15560                 // If either of the arguments is one, the result is the other and no overflow occured.
  15561                 // Otherwise, if either of the arguments is undefined, both results are undefined.
  15562                 const scalar_one = try pt.intValue(dest_ty.scalarType(zcu), 1);
  15563                 if (maybe_lhs_val) |lhs_val| {
  15564                     if (!lhs_val.isUndef(zcu)) {
  15565                         if (try lhs_val.compareAllWithZeroSema(.eq, pt)) {
  15566                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15567                         } else if (try sema.compareAll(lhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) {
  15568                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
  15569                         }
  15570                     }
  15571                 }
  15572 
  15573                 if (maybe_rhs_val) |rhs_val| {
  15574                     if (!rhs_val.isUndef(zcu)) {
  15575                         if (try rhs_val.compareAllWithZeroSema(.eq, pt)) {
  15576                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
  15577                         } else if (try sema.compareAll(rhs_val, .eq, try sema.splat(dest_ty, scalar_one), dest_ty)) {
  15578                             break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15579                         }
  15580                     }
  15581                 }
  15582 
  15583                 if (maybe_lhs_val) |lhs_val| {
  15584                     if (maybe_rhs_val) |rhs_val| {
  15585                         if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
  15586                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15587                         }
  15588 
  15589                         const result = try arith.mulWithOverflow(sema, dest_ty, lhs_val, rhs_val);
  15590                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15591                     }
  15592                 }
  15593             },
  15594             .shl_with_overflow => {
  15595                 // If lhs is zero, the result is zero and no overflow occurred.
  15596                 // If rhs is zero, the result is lhs (even if undefined) and no overflow occurred.
  15597                 // Oterhwise if either of the arguments is undefined, both results are undefined.
  15598                 if (maybe_lhs_val) |lhs_val| {
  15599                     if (!lhs_val.isUndef(zcu) and (try lhs_val.compareAllWithZeroSema(.eq, pt))) {
  15600                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15601                     }
  15602                 }
  15603                 if (maybe_rhs_val) |rhs_val| {
  15604                     if (!rhs_val.isUndef(zcu) and (try rhs_val.compareAllWithZeroSema(.eq, pt))) {
  15605                         break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
  15606                     }
  15607                 }
  15608                 if (maybe_lhs_val) |lhs_val| {
  15609                     if (maybe_rhs_val) |rhs_val| {
  15610                         if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
  15611                             break :result .{ .overflow_bit = Value.undef, .wrapped = Value.undef };
  15612                         }
  15613 
  15614                         const result = try lhs_val.shlWithOverflow(rhs_val, dest_ty, sema.arena, pt);
  15615                         break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
  15616                     }
  15617                 }
  15618             },
  15619             else => unreachable,
  15620         }
  15621 
  15622         const air_tag: Air.Inst.Tag = switch (zir_tag) {
  15623             .add_with_overflow => .add_with_overflow,
  15624             .mul_with_overflow => .mul_with_overflow,
  15625             .sub_with_overflow => .sub_with_overflow,
  15626             .shl_with_overflow => .shl_with_overflow,
  15627             else => unreachable,
  15628         };
  15629 
  15630         const runtime_src = if (maybe_lhs_val == null) lhs_src else rhs_src;
  15631         try sema.requireRuntimeBlock(block, src, runtime_src);
  15632 
  15633         return block.addInst(.{
  15634             .tag = air_tag,
  15635             .data = .{ .ty_pl = .{
  15636                 .ty = Air.internedToRef(tuple_ty.toIntern()),
  15637                 .payload = try block.sema.addExtra(Air.Bin{
  15638                     .lhs = lhs,
  15639                     .rhs = rhs,
  15640                 }),
  15641             } },
  15642         });
  15643     };
  15644 
  15645     if (result.inst != .none) {
  15646         if (try sema.resolveValue(result.inst)) |some| {
  15647             result.wrapped = some;
  15648             result.inst = .none;
  15649         }
  15650     }
  15651 
  15652     if (result.inst == .none) {
  15653         return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  15654             .ty = tuple_ty.toIntern(),
  15655             .storage = .{ .elems = &.{
  15656                 result.wrapped.toIntern(),
  15657                 result.overflow_bit.toIntern(),
  15658             } },
  15659         } })));
  15660     }
  15661 
  15662     const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2);
  15663     element_refs[0] = result.inst;
  15664     element_refs[1] = Air.internedToRef(result.overflow_bit.toIntern());
  15665     return block.addAggregateInit(tuple_ty, element_refs);
  15666 }
  15667 
  15668 fn splat(sema: *Sema, ty: Type, val: Value) !Value {
  15669     const pt = sema.pt;
  15670     const zcu = pt.zcu;
  15671     if (ty.zigTypeTag(zcu) != .vector) return val;
  15672     const repeated = try pt.intern(.{ .aggregate = .{
  15673         .ty = ty.toIntern(),
  15674         .storage = .{ .repeated_elem = val.toIntern() },
  15675     } });
  15676     return Value.fromInterned(repeated);
  15677 }
  15678 
  15679 fn analyzeArithmetic(
  15680     sema: *Sema,
  15681     block: *Block,
  15682     /// TODO performance investigation: make this comptime?
  15683     zir_tag: Zir.Inst.Tag,
  15684     lhs: Air.Inst.Ref,
  15685     rhs: Air.Inst.Ref,
  15686     src: LazySrcLoc,
  15687     lhs_src: LazySrcLoc,
  15688     rhs_src: LazySrcLoc,
  15689     want_safety: bool,
  15690 ) CompileError!Air.Inst.Ref {
  15691     const pt = sema.pt;
  15692     const zcu = pt.zcu;
  15693     const lhs_ty = sema.typeOf(lhs);
  15694     const rhs_ty = sema.typeOf(rhs);
  15695     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  15696     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  15697     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  15698 
  15699     if (lhs_zig_ty_tag == .pointer) {
  15700         if (rhs_zig_ty_tag == .pointer) {
  15701             if (lhs_ty.ptrSize(zcu) != .slice and rhs_ty.ptrSize(zcu) != .slice) {
  15702                 if (zir_tag != .sub) {
  15703                     return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction");
  15704                 }
  15705                 if (!lhs_ty.elemType2(zcu).eql(rhs_ty.elemType2(zcu), zcu)) {
  15706                     return sema.fail(block, src, "incompatible pointer arithmetic operands '{f}' and '{f}'", .{
  15707                         lhs_ty.fmt(pt), rhs_ty.fmt(pt),
  15708                     });
  15709                 }
  15710 
  15711                 const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu);
  15712                 if (elem_size == 0) {
  15713                     return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{
  15714                         lhs_ty.elemType2(zcu).fmt(pt),
  15715                     });
  15716                 }
  15717 
  15718                 const runtime_src = runtime_src: {
  15719                     if (try sema.resolveValue(lhs)) |lhs_value| {
  15720                         if (try sema.resolveValue(rhs)) |rhs_value| {
  15721                             const lhs_ptr = switch (zcu.intern_pool.indexToKey(lhs_value.toIntern())) {
  15722                                 .undef => return sema.failWithUseOfUndef(block, lhs_src),
  15723                                 .ptr => |ptr| ptr,
  15724                                 else => unreachable,
  15725                             };
  15726                             const rhs_ptr = switch (zcu.intern_pool.indexToKey(rhs_value.toIntern())) {
  15727                                 .undef => return sema.failWithUseOfUndef(block, rhs_src),
  15728                                 .ptr => |ptr| ptr,
  15729                                 else => unreachable,
  15730                             };
  15731                             // Make sure the pointers point to the same data.
  15732                             if (!lhs_ptr.base_addr.eql(rhs_ptr.base_addr)) break :runtime_src src;
  15733                             const address = std.math.sub(u64, lhs_ptr.byte_offset, rhs_ptr.byte_offset) catch
  15734                                 return sema.fail(block, src, "operation results in overflow", .{});
  15735                             const result = address / elem_size;
  15736                             return try pt.intRef(.usize, result);
  15737                         } else {
  15738                             break :runtime_src lhs_src;
  15739                         }
  15740                     } else {
  15741                         break :runtime_src rhs_src;
  15742                     }
  15743                 };
  15744 
  15745                 try sema.requireRuntimeBlock(block, src, runtime_src);
  15746                 try sema.checkLogicalPtrOperation(block, src, lhs_ty);
  15747                 try sema.checkLogicalPtrOperation(block, src, rhs_ty);
  15748                 const lhs_int = try block.addBitCast(.usize, lhs);
  15749                 const rhs_int = try block.addBitCast(.usize, rhs);
  15750                 const address = try block.addBinOp(.sub_wrap, lhs_int, rhs_int);
  15751                 return try block.addBinOp(.div_exact, address, try pt.intRef(.usize, elem_size));
  15752             }
  15753         } else {
  15754             switch (lhs_ty.ptrSize(zcu)) {
  15755                 .one, .slice => {},
  15756                 .many, .c => {
  15757                     const air_tag: Air.Inst.Tag = switch (zir_tag) {
  15758                         .add => .ptr_add,
  15759                         .sub => .ptr_sub,
  15760                         else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
  15761                     };
  15762 
  15763                     if (!try lhs_ty.elemType2(zcu).hasRuntimeBitsSema(pt)) {
  15764                         return sema.fail(block, src, "pointer arithmetic requires element type '{f}' to have runtime bits", .{
  15765                             lhs_ty.elemType2(zcu).fmt(pt),
  15766                         });
  15767                     }
  15768                     return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, lhs_src, rhs_src);
  15769                 },
  15770             }
  15771         }
  15772     }
  15773 
  15774     const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
  15775         .override = &.{ lhs_src, rhs_src },
  15776     });
  15777 
  15778     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  15779     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  15780 
  15781     const scalar_type = resolved_type.scalarType(zcu);
  15782     const scalar_tag = scalar_type.zigTypeTag(zcu);
  15783 
  15784     try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag);
  15785 
  15786     // The rules we'll implement below are as follows:
  15787     //
  15788     // * If both operands are comptime-known, we call the corresponding function in `arith` to get
  15789     //   the comptime-known result. Inputs which would be IB at runtime are compile errors.
  15790     //
  15791     // * Otherwise, if one operand is comptime-known `undefined`, we trigger a compile error if this
  15792     //   operator can ever possibly trigger IB, or otherwise return comptime-known `undefined`.
  15793     //
  15794     // * No other comptime operand detemines a comptime result; e.g. `0 * x` isn't always `0` because
  15795     //   of `undefined`. Therefore, the remaining cases all become runtime operations.
  15796 
  15797     const is_int = switch (scalar_tag) {
  15798         .int, .comptime_int => true,
  15799         .float, .comptime_float => false,
  15800         else => unreachable,
  15801     };
  15802 
  15803     const maybe_lhs_val = try sema.resolveValueResolveLazy(casted_lhs);
  15804     const maybe_rhs_val = try sema.resolveValueResolveLazy(casted_rhs);
  15805 
  15806     if (maybe_lhs_val) |lhs_val| {
  15807         if (maybe_rhs_val) |rhs_val| {
  15808             const result_val = switch (zir_tag) {
  15809                 .add, .add_unsafe => try arith.add(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src),
  15810                 .addwrap => try arith.addWrap(sema, resolved_type, lhs_val, rhs_val),
  15811                 .add_sat => try arith.addSat(sema, resolved_type, lhs_val, rhs_val),
  15812                 .sub => try arith.sub(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src),
  15813                 .subwrap => try arith.subWrap(sema, resolved_type, lhs_val, rhs_val),
  15814                 .sub_sat => try arith.subSat(sema, resolved_type, lhs_val, rhs_val),
  15815                 .mul => try arith.mul(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src),
  15816                 .mulwrap => try arith.mulWrap(sema, resolved_type, lhs_val, rhs_val),
  15817                 .mul_sat => try arith.mulSat(sema, resolved_type, lhs_val, rhs_val),
  15818                 else => unreachable,
  15819             };
  15820             return Air.internedToRef(result_val.toIntern());
  15821         }
  15822     }
  15823 
  15824     const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag, const allow_undef: bool = switch (zir_tag) {
  15825         .add, .add_unsafe => .{ if (block.float_mode == .optimized) .add_optimized else .add, .add_safe, !is_int },
  15826         .addwrap => .{ .add_wrap, .add_wrap, true },
  15827         .add_sat => .{ .add_sat, .add_sat, true },
  15828         .sub => .{ if (block.float_mode == .optimized) .sub_optimized else .sub, .sub_safe, !is_int },
  15829         .subwrap => .{ .sub_wrap, .sub_wrap, true },
  15830         .sub_sat => .{ .sub_sat, .sub_sat, true },
  15831         .mul => .{ if (block.float_mode == .optimized) .mul_optimized else .mul, .mul_safe, !is_int },
  15832         .mulwrap => .{ .mul_wrap, .mul_wrap, true },
  15833         .mul_sat => .{ .mul_sat, .mul_sat, true },
  15834         else => unreachable,
  15835     };
  15836 
  15837     if (allow_undef) {
  15838         if (maybe_lhs_val) |lhs_val| {
  15839             if (lhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15840         }
  15841         if (maybe_rhs_val) |rhs_val| {
  15842             if (rhs_val.isUndefDeep(zcu)) return pt.undefRef(resolved_type);
  15843         }
  15844     } else {
  15845         if (maybe_lhs_val) |lhs_val| {
  15846             if (lhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, lhs_src);
  15847         }
  15848         if (maybe_rhs_val) |rhs_val| {
  15849             if (rhs_val.anyScalarIsUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src);
  15850         }
  15851     }
  15852 
  15853     if (block.wantSafety() and want_safety and scalar_tag == .int) {
  15854         if (air_tag != air_tag_safe) try sema.preparePanicId(src, .integer_overflow);
  15855         return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs);
  15856     }
  15857     return block.addBinOp(air_tag, casted_lhs, casted_rhs);
  15858 }
  15859 
  15860 fn analyzePtrArithmetic(
  15861     sema: *Sema,
  15862     block: *Block,
  15863     op_src: LazySrcLoc,
  15864     ptr: Air.Inst.Ref,
  15865     uncasted_offset: Air.Inst.Ref,
  15866     air_tag: Air.Inst.Tag,
  15867     ptr_src: LazySrcLoc,
  15868     offset_src: LazySrcLoc,
  15869 ) CompileError!Air.Inst.Ref {
  15870     // TODO if the operand is comptime-known to be negative, or is a negative int,
  15871     // coerce to isize instead of usize.
  15872     const offset = try sema.coerce(block, .usize, uncasted_offset, offset_src);
  15873     const pt = sema.pt;
  15874     const zcu = pt.zcu;
  15875     const opt_ptr_val = try sema.resolveValue(ptr);
  15876     const opt_off_val = try sema.resolveDefinedValue(block, offset_src, offset);
  15877     const ptr_ty = sema.typeOf(ptr);
  15878     const ptr_info = ptr_ty.ptrInfo(zcu);
  15879     assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c);
  15880 
  15881     const new_ptr_ty = t: {
  15882         // Calculate the new pointer alignment.
  15883         // This code is duplicated in `Type.elemPtrType`.
  15884         if (ptr_info.flags.alignment == .none) {
  15885             // ABI-aligned pointer. Any pointer arithmetic maintains the same ABI-alignedness.
  15886             break :t ptr_ty;
  15887         }
  15888         // If the addend is not a comptime-known value we can still count on
  15889         // it being a multiple of the type size.
  15890         const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt);
  15891         const addend = if (opt_off_val) |off_val| a: {
  15892             const off_int = try sema.usizeCast(block, offset_src, try off_val.toUnsignedIntSema(pt));
  15893             break :a elem_size * off_int;
  15894         } else elem_size;
  15895 
  15896         // The resulting pointer is aligned to the lcd between the offset (an
  15897         // arbitrary number) and the alignment factor (always a power of two,
  15898         // non zero).
  15899         const new_align: Alignment = @enumFromInt(@min(
  15900             @ctz(addend),
  15901             @intFromEnum(ptr_info.flags.alignment),
  15902         ));
  15903         assert(new_align != .none);
  15904 
  15905         break :t try pt.ptrTypeSema(.{
  15906             .child = ptr_info.child,
  15907             .sentinel = ptr_info.sentinel,
  15908             .flags = .{
  15909                 .size = ptr_info.flags.size,
  15910                 .alignment = new_align,
  15911                 .is_const = ptr_info.flags.is_const,
  15912                 .is_volatile = ptr_info.flags.is_volatile,
  15913                 .is_allowzero = ptr_info.flags.is_allowzero,
  15914                 .address_space = ptr_info.flags.address_space,
  15915             },
  15916         });
  15917     };
  15918 
  15919     const runtime_src = rs: {
  15920         if (opt_ptr_val) |ptr_val| {
  15921             if (opt_off_val) |offset_val| {
  15922                 if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty);
  15923 
  15924                 const offset_int = try sema.usizeCast(block, offset_src, try offset_val.toUnsignedIntSema(pt));
  15925                 if (offset_int == 0) return ptr;
  15926                 if (air_tag == .ptr_sub) {
  15927                     const elem_size = try Type.fromInterned(ptr_info.child).abiSizeSema(pt);
  15928                     const new_ptr_val = try sema.ptrSubtract(block, op_src, ptr_val, offset_int * elem_size, new_ptr_ty);
  15929                     return Air.internedToRef(new_ptr_val.toIntern());
  15930                 } else {
  15931                     const new_ptr_val = try pt.getCoerced(try ptr_val.ptrElem(offset_int, pt), new_ptr_ty);
  15932                     return Air.internedToRef(new_ptr_val.toIntern());
  15933                 }
  15934             } else break :rs offset_src;
  15935         } else break :rs ptr_src;
  15936     };
  15937 
  15938     try sema.requireRuntimeBlock(block, op_src, runtime_src);
  15939     try sema.checkLogicalPtrOperation(block, op_src, ptr_ty);
  15940 
  15941     return block.addInst(.{
  15942         .tag = air_tag,
  15943         .data = .{ .ty_pl = .{
  15944             .ty = Air.internedToRef(new_ptr_ty.toIntern()),
  15945             .payload = try sema.addExtra(Air.Bin{
  15946                 .lhs = ptr,
  15947                 .rhs = offset,
  15948             }),
  15949         } },
  15950     });
  15951 }
  15952 
  15953 fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  15954     const tracy = trace(@src());
  15955     defer tracy.end();
  15956 
  15957     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  15958     const src = block.nodeOffset(inst_data.src_node);
  15959     const ptr_src = src; // TODO better source location
  15960     const ptr = try sema.resolveInst(inst_data.operand);
  15961     return sema.analyzeLoad(block, src, ptr, ptr_src);
  15962 }
  15963 
  15964 fn zirAsm(
  15965     sema: *Sema,
  15966     block: *Block,
  15967     extended: Zir.Inst.Extended.InstData,
  15968     tmpl_is_expr: bool,
  15969 ) CompileError!Air.Inst.Ref {
  15970     const tracy = trace(@src());
  15971     defer tracy.end();
  15972 
  15973     const pt = sema.pt;
  15974     const zcu = pt.zcu;
  15975     const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
  15976     const src = block.nodeOffset(extra.data.src_node);
  15977     const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node });
  15978     const small: Zir.Inst.Asm.Small = @bitCast(extended.small);
  15979     const outputs_len = small.outputs_len;
  15980     const inputs_len = small.inputs_len;
  15981     const is_volatile = small.is_volatile;
  15982     const is_global_assembly = sema.func_index == .none;
  15983     const zir_tags = sema.code.instructions.items(.tag);
  15984 
  15985     const asm_source: []const u8 = if (tmpl_is_expr) s: {
  15986         const tmpl: Zir.Inst.Ref = @enumFromInt(@intFromEnum(extra.data.asm_source));
  15987         break :s try sema.resolveConstString(block, src, tmpl, .{ .simple = .inline_assembly_code });
  15988     } else sema.code.nullTerminatedString(extra.data.asm_source);
  15989 
  15990     if (is_global_assembly) {
  15991         if (outputs_len != 0) {
  15992             return sema.fail(block, src, "module-level assembly does not support outputs", .{});
  15993         }
  15994         if (inputs_len != 0) {
  15995             return sema.fail(block, src, "module-level assembly does not support inputs", .{});
  15996         }
  15997         if (extra.data.clobbers != .none) {
  15998             return sema.fail(block, src, "module-level assembly does not support clobbers", .{});
  15999         }
  16000         if (is_volatile) {
  16001             return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{});
  16002         }
  16003         try zcu.addGlobalAssembly(sema.owner, asm_source);
  16004         return .void_value;
  16005     }
  16006 
  16007     try sema.requireRuntimeBlock(block, src, null);
  16008 
  16009     var extra_i = extra.end;
  16010     var output_type_bits = extra.data.output_type_bits;
  16011     var needed_capacity: usize = @typeInfo(Air.Asm).@"struct".fields.len + outputs_len + inputs_len;
  16012 
  16013     const ConstraintName = struct { c: []const u8, n: []const u8 };
  16014     const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len);
  16015     const outputs = try sema.arena.alloc(ConstraintName, outputs_len);
  16016     var expr_ty = Air.Inst.Ref.void_type;
  16017 
  16018     for (out_args, 0..) |*arg, out_i| {
  16019         const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);
  16020         extra_i = output.end;
  16021 
  16022         const is_type = @as(u1, @truncate(output_type_bits)) != 0;
  16023         output_type_bits >>= 1;
  16024 
  16025         if (is_type) {
  16026             // Indicate the output is the asm instruction return value.
  16027             arg.* = .none;
  16028             const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand);
  16029             expr_ty = Air.internedToRef(out_ty.toIntern());
  16030         } else {
  16031             arg.* = try sema.resolveInst(output.data.operand);
  16032         }
  16033 
  16034         const constraint = sema.code.nullTerminatedString(output.data.constraint);
  16035         const name = sema.code.nullTerminatedString(output.data.name);
  16036         needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
  16037 
  16038         if (output.data.operand.toIndex()) |index| {
  16039             if (zir_tags[@intFromEnum(index)] == .ref) {
  16040                 // TODO: better error location; it would be even nicer if there were notes that pointed at the output and the variable definition
  16041                 return sema.fail(block, src, "asm cannot output to const local '{s}'", .{name});
  16042             }
  16043         }
  16044 
  16045         outputs[out_i] = .{ .c = constraint, .n = name };
  16046     }
  16047 
  16048     const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len);
  16049     const inputs = try sema.arena.alloc(ConstraintName, inputs_len);
  16050 
  16051     for (args, 0..) |*arg, arg_i| {
  16052         const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i);
  16053         extra_i = input.end;
  16054 
  16055         const uncasted_arg = try sema.resolveInst(input.data.operand);
  16056         const uncasted_arg_ty = sema.typeOf(uncasted_arg);
  16057         switch (uncasted_arg_ty.zigTypeTag(zcu)) {
  16058             .comptime_int => arg.* = try sema.coerce(block, .usize, uncasted_arg, src),
  16059             .comptime_float => arg.* = try sema.coerce(block, .f64, uncasted_arg, src),
  16060             else => {
  16061                 arg.* = uncasted_arg;
  16062             },
  16063         }
  16064 
  16065         const constraint = sema.code.nullTerminatedString(input.data.constraint);
  16066         const name = sema.code.nullTerminatedString(input.data.name);
  16067         needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
  16068         inputs[arg_i] = .{ .c = constraint, .n = name };
  16069     }
  16070 
  16071     const clobbers = if (extra.data.clobbers == .none) empty: {
  16072         const clobbers_ty = try sema.getBuiltinType(src, .@"assembly.Clobbers");
  16073         break :empty try sema.structInitEmpty(block, clobbers_ty, src, src);
  16074     } else try sema.resolveInst(extra.data.clobbers); // Already coerced by AstGen.
  16075     const clobbers_val = try sema.resolveConstDefinedValue(block, src, clobbers, .{ .simple = .clobber });
  16076     needed_capacity += asm_source.len / 4 + 1;
  16077 
  16078     const gpa = sema.gpa;
  16079     try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
  16080     const asm_air = try block.addInst(.{
  16081         .tag = .assembly,
  16082         .data = .{ .ty_pl = .{
  16083             .ty = expr_ty,
  16084             .payload = sema.addExtraAssumeCapacity(Air.Asm{
  16085                 .source_len = @intCast(asm_source.len),
  16086                 .inputs_len = @intCast(args.len),
  16087                 .clobbers = clobbers_val.toIntern(),
  16088                 .flags = .{
  16089                     .is_volatile = is_volatile,
  16090                     .outputs_len = outputs_len,
  16091                 },
  16092             }),
  16093         } },
  16094     });
  16095     sema.appendRefsAssumeCapacity(out_args);
  16096     sema.appendRefsAssumeCapacity(args);
  16097     for (outputs) |o| {
  16098         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16099         @memcpy(buffer[0..o.c.len], o.c);
  16100         buffer[o.c.len] = 0;
  16101         @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n);
  16102         buffer[o.c.len + 1 + o.n.len] = 0;
  16103         sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4;
  16104     }
  16105     for (inputs) |input| {
  16106         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16107         @memcpy(buffer[0..input.c.len], input.c);
  16108         buffer[input.c.len] = 0;
  16109         @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n);
  16110         buffer[input.c.len + 1 + input.n.len] = 0;
  16111         sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4;
  16112     }
  16113     {
  16114         const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
  16115         @memcpy(buffer[0..asm_source.len], asm_source);
  16116         buffer[asm_source.len] = 0;
  16117         sema.air_extra.items.len += asm_source.len / 4 + 1;
  16118     }
  16119     return asm_air;
  16120 }
  16121 
  16122 /// Only called for equality operators. See also `zirCmp`.
  16123 fn zirCmpEq(
  16124     sema: *Sema,
  16125     block: *Block,
  16126     inst: Zir.Inst.Index,
  16127     op: std.math.CompareOperator,
  16128     air_tag: Air.Inst.Tag,
  16129 ) CompileError!Air.Inst.Ref {
  16130     const tracy = trace(@src());
  16131     defer tracy.end();
  16132 
  16133     const pt = sema.pt;
  16134     const zcu = pt.zcu;
  16135     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  16136     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  16137     const src: LazySrcLoc = block.nodeOffset(inst_data.src_node);
  16138     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  16139     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  16140     const lhs = try sema.resolveInst(extra.lhs);
  16141     const rhs = try sema.resolveInst(extra.rhs);
  16142 
  16143     const lhs_ty = sema.typeOf(lhs);
  16144     const rhs_ty = sema.typeOf(rhs);
  16145     const lhs_ty_tag = lhs_ty.zigTypeTag(zcu);
  16146     const rhs_ty_tag = rhs_ty.zigTypeTag(zcu);
  16147     if (lhs_ty_tag == .null and rhs_ty_tag == .null) {
  16148         // null == null, null != null
  16149         return if (op == .eq) .bool_true else .bool_false;
  16150     }
  16151 
  16152     // comparing null with optionals
  16153     if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) {
  16154         return sema.analyzeIsNull(block, rhs, op == .neq);
  16155     }
  16156     if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) {
  16157         return sema.analyzeIsNull(block, lhs, op == .neq);
  16158     }
  16159 
  16160     if (lhs_ty_tag == .null or rhs_ty_tag == .null) {
  16161         const non_null_type = if (lhs_ty_tag == .null) rhs_ty else lhs_ty;
  16162         return sema.fail(block, src, "comparison of '{f}' with null", .{non_null_type.fmt(pt)});
  16163     }
  16164 
  16165     if (lhs_ty_tag == .@"union" and (rhs_ty_tag == .enum_literal or rhs_ty_tag == .@"enum")) {
  16166         return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op);
  16167     }
  16168     if (rhs_ty_tag == .@"union" and (lhs_ty_tag == .enum_literal or lhs_ty_tag == .@"enum")) {
  16169         return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op);
  16170     }
  16171 
  16172     if (lhs_ty_tag == .error_set and rhs_ty_tag == .error_set) {
  16173         const runtime_src: LazySrcLoc = src: {
  16174             if (try sema.resolveValue(lhs)) |lval| {
  16175                 if (try sema.resolveValue(rhs)) |rval| {
  16176                     if (lval.isUndef(zcu) or rval.isUndef(zcu)) return .undef_bool;
  16177                     const lkey = zcu.intern_pool.indexToKey(lval.toIntern());
  16178                     const rkey = zcu.intern_pool.indexToKey(rval.toIntern());
  16179                     return if ((lkey.err.name == rkey.err.name) == (op == .eq))
  16180                         .bool_true
  16181                     else
  16182                         .bool_false;
  16183                 } else {
  16184                     break :src rhs_src;
  16185                 }
  16186             } else {
  16187                 break :src lhs_src;
  16188             }
  16189         };
  16190         try sema.requireRuntimeBlock(block, src, runtime_src);
  16191         return block.addBinOp(air_tag, lhs, rhs);
  16192     }
  16193     if (lhs_ty_tag == .type and rhs_ty_tag == .type) {
  16194         const lhs_as_type = try sema.analyzeAsType(block, lhs_src, lhs);
  16195         const rhs_as_type = try sema.analyzeAsType(block, rhs_src, rhs);
  16196         return if (lhs_as_type.eql(rhs_as_type, zcu) == (op == .eq)) .bool_true else .bool_false;
  16197     }
  16198     return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true);
  16199 }
  16200 
  16201 fn analyzeCmpUnionTag(
  16202     sema: *Sema,
  16203     block: *Block,
  16204     src: LazySrcLoc,
  16205     un: Air.Inst.Ref,
  16206     un_src: LazySrcLoc,
  16207     tag: Air.Inst.Ref,
  16208     tag_src: LazySrcLoc,
  16209     op: std.math.CompareOperator,
  16210 ) CompileError!Air.Inst.Ref {
  16211     const pt = sema.pt;
  16212     const zcu = pt.zcu;
  16213     const union_ty = sema.typeOf(un);
  16214     try union_ty.resolveFields(pt);
  16215     const union_tag_ty = union_ty.unionTagType(zcu) orelse {
  16216         const msg = msg: {
  16217             const msg = try sema.errMsg(un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
  16218             errdefer msg.destroy(sema.gpa);
  16219             try sema.errNote(union_ty.srcLoc(zcu), msg, "union '{f}' is not a tagged union", .{union_ty.fmt(pt)});
  16220             break :msg msg;
  16221         };
  16222         return sema.failWithOwnedErrorMsg(block, msg);
  16223     };
  16224     // Coerce both the union and the tag to the union's tag type, and then execute the
  16225     // enum comparison codepath.
  16226     const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src);
  16227     const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src);
  16228 
  16229     if (try sema.resolveValue(coerced_tag)) |enum_val| {
  16230         if (enum_val.isUndef(zcu)) return .undef_bool;
  16231         const field_ty = union_ty.unionFieldType(enum_val, zcu).?;
  16232         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  16233             return .bool_false;
  16234         }
  16235     }
  16236 
  16237     return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src);
  16238 }
  16239 
  16240 /// Only called for non-equality operators. See also `zirCmpEq`.
  16241 fn zirCmp(
  16242     sema: *Sema,
  16243     block: *Block,
  16244     inst: Zir.Inst.Index,
  16245     op: std.math.CompareOperator,
  16246 ) CompileError!Air.Inst.Ref {
  16247     const tracy = trace(@src());
  16248     defer tracy.end();
  16249 
  16250     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  16251     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  16252     const src: LazySrcLoc = block.nodeOffset(inst_data.src_node);
  16253     const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  16254     const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  16255     const lhs = try sema.resolveInst(extra.lhs);
  16256     const rhs = try sema.resolveInst(extra.rhs);
  16257     return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false);
  16258 }
  16259 
  16260 fn analyzeCmp(
  16261     sema: *Sema,
  16262     block: *Block,
  16263     src: LazySrcLoc,
  16264     lhs: Air.Inst.Ref,
  16265     rhs: Air.Inst.Ref,
  16266     op: std.math.CompareOperator,
  16267     lhs_src: LazySrcLoc,
  16268     rhs_src: LazySrcLoc,
  16269     is_equality_cmp: bool,
  16270 ) CompileError!Air.Inst.Ref {
  16271     const pt = sema.pt;
  16272     const zcu = pt.zcu;
  16273     const lhs_ty = sema.typeOf(lhs);
  16274     const rhs_ty = sema.typeOf(rhs);
  16275     if (lhs_ty.zigTypeTag(zcu) != .optional and rhs_ty.zigTypeTag(zcu) != .optional) {
  16276         try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  16277     }
  16278 
  16279     if (lhs_ty.zigTypeTag(zcu) == .vector and rhs_ty.zigTypeTag(zcu) == .vector) {
  16280         return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src);
  16281     }
  16282     if (lhs_ty.isNumeric(zcu) and rhs_ty.isNumeric(zcu)) {
  16283         // This operation allows any combination of integer and float types, regardless of the
  16284         // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
  16285         // numeric types.
  16286         return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src);
  16287     }
  16288     if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_union and rhs_ty.zigTypeTag(zcu) == .error_set) {
  16289         if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
  16290             if (lhs_val.errorUnionIsPayload(zcu)) return .bool_false;
  16291         }
  16292         const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs);
  16293         return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src);
  16294     }
  16295     if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_set and rhs_ty.zigTypeTag(zcu) == .error_union) {
  16296         if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
  16297             if (rhs_val.errorUnionIsPayload(zcu)) return .bool_false;
  16298         }
  16299         const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs);
  16300         return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src);
  16301     }
  16302     const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
  16303     const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
  16304     if (!resolved_type.isSelfComparable(zcu, is_equality_cmp)) {
  16305         return sema.fail(block, src, "operator {s} not allowed for type '{f}'", .{
  16306             compareOperatorName(op), resolved_type.fmt(pt),
  16307         });
  16308     }
  16309     const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
  16310     const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
  16311     return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src);
  16312 }
  16313 
  16314 fn compareOperatorName(comp: std.math.CompareOperator) []const u8 {
  16315     return switch (comp) {
  16316         .lt => "<",
  16317         .lte => "<=",
  16318         .eq => "==",
  16319         .gte => ">=",
  16320         .gt => ">",
  16321         .neq => "!=",
  16322     };
  16323 }
  16324 
  16325 fn cmpSelf(
  16326     sema: *Sema,
  16327     block: *Block,
  16328     src: LazySrcLoc,
  16329     casted_lhs: Air.Inst.Ref,
  16330     casted_rhs: Air.Inst.Ref,
  16331     op: std.math.CompareOperator,
  16332     lhs_src: LazySrcLoc,
  16333     rhs_src: LazySrcLoc,
  16334 ) CompileError!Air.Inst.Ref {
  16335     const pt = sema.pt;
  16336     const zcu = pt.zcu;
  16337     const resolved_type = sema.typeOf(casted_lhs);
  16338 
  16339     const maybe_lhs_val = try sema.resolveValue(casted_lhs);
  16340     const maybe_rhs_val = try sema.resolveValue(casted_rhs);
  16341     if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
  16342     if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
  16343 
  16344     const runtime_src: LazySrcLoc = src: {
  16345         if (maybe_lhs_val) |lhs_val| {
  16346             if (maybe_rhs_val) |rhs_val| {
  16347                 if (resolved_type.zigTypeTag(zcu) == .vector) {
  16348                     const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);
  16349                     return Air.internedToRef(cmp_val.toIntern());
  16350                 }
  16351 
  16352                 return if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type))
  16353                     .bool_true
  16354                 else
  16355                     .bool_false;
  16356             } else {
  16357                 if (resolved_type.zigTypeTag(zcu) == .bool) {
  16358                     // We can lower bool eq/neq more efficiently.
  16359                     return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src);
  16360                 }
  16361                 break :src rhs_src;
  16362             }
  16363         } else {
  16364             // For bools, we still check the other operand, because we can lower
  16365             // bool eq/neq more efficiently.
  16366             if (resolved_type.zigTypeTag(zcu) == .bool) {
  16367                 if (maybe_rhs_val) |rhs_val| {
  16368                     return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
  16369                 }
  16370             }
  16371             break :src lhs_src;
  16372         }
  16373     };
  16374     try sema.requireRuntimeBlock(block, src, runtime_src);
  16375     if (resolved_type.zigTypeTag(zcu) == .vector) {
  16376         return block.addCmpVector(casted_lhs, casted_rhs, op);
  16377     }
  16378     const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized);
  16379     return block.addBinOp(tag, casted_lhs, casted_rhs);
  16380 }
  16381 
  16382 /// cmp_eq (x, false) => not(x)
  16383 /// cmp_eq (x, true ) => x
  16384 /// cmp_neq(x, false) => x
  16385 /// cmp_neq(x, true ) => not(x)
  16386 fn runtimeBoolCmp(
  16387     sema: *Sema,
  16388     block: *Block,
  16389     src: LazySrcLoc,
  16390     op: std.math.CompareOperator,
  16391     lhs: Air.Inst.Ref,
  16392     rhs: bool,
  16393     runtime_src: LazySrcLoc,
  16394 ) CompileError!Air.Inst.Ref {
  16395     if ((op == .neq) == rhs) {
  16396         try sema.requireRuntimeBlock(block, src, runtime_src);
  16397         return block.addTyOp(.not, .bool, lhs);
  16398     } else {
  16399         return lhs;
  16400     }
  16401 }
  16402 
  16403 fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16404     const pt = sema.pt;
  16405     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16406     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  16407     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
  16408     switch (ty.zigTypeTag(pt.zcu)) {
  16409         .@"fn",
  16410         .noreturn,
  16411         .undefined,
  16412         .null,
  16413         .@"opaque",
  16414         => return sema.fail(block, operand_src, "no size available for type '{f}'", .{ty.fmt(pt)}),
  16415 
  16416         .type,
  16417         .enum_literal,
  16418         .comptime_float,
  16419         .comptime_int,
  16420         .void,
  16421         => return .zero,
  16422 
  16423         .bool,
  16424         .int,
  16425         .float,
  16426         .pointer,
  16427         .array,
  16428         .@"struct",
  16429         .optional,
  16430         .error_union,
  16431         .error_set,
  16432         .@"enum",
  16433         .@"union",
  16434         .vector,
  16435         .frame,
  16436         .@"anyframe",
  16437         => {},
  16438     }
  16439     const val = try ty.abiSizeLazy(pt);
  16440     return Air.internedToRef(val.toIntern());
  16441 }
  16442 
  16443 fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16444     const pt = sema.pt;
  16445     const zcu = pt.zcu;
  16446     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16447     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  16448     const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
  16449     switch (operand_ty.zigTypeTag(zcu)) {
  16450         .@"fn",
  16451         .noreturn,
  16452         .undefined,
  16453         .null,
  16454         .@"opaque",
  16455         => return sema.fail(block, operand_src, "no size available for type '{f}'", .{operand_ty.fmt(pt)}),
  16456 
  16457         .type,
  16458         .enum_literal,
  16459         .comptime_float,
  16460         .comptime_int,
  16461         .void,
  16462         => return .zero,
  16463 
  16464         .bool,
  16465         .int,
  16466         .float,
  16467         .pointer,
  16468         .array,
  16469         .@"struct",
  16470         .optional,
  16471         .error_union,
  16472         .error_set,
  16473         .@"enum",
  16474         .@"union",
  16475         .vector,
  16476         .frame,
  16477         .@"anyframe",
  16478         => {},
  16479     }
  16480     const bit_size = try operand_ty.bitSizeSema(pt);
  16481     return pt.intRef(.comptime_int, bit_size);
  16482 }
  16483 
  16484 fn zirThis(
  16485     sema: *Sema,
  16486     block: *Block,
  16487     extended: Zir.Inst.Extended.InstData,
  16488 ) CompileError!Air.Inst.Ref {
  16489     _ = extended;
  16490     const pt = sema.pt;
  16491     const zcu = pt.zcu;
  16492     const namespace = pt.zcu.namespacePtr(block.namespace);
  16493 
  16494     switch (pt.zcu.intern_pool.indexToKey(namespace.owner_type)) {
  16495         .opaque_type => {
  16496             // Opaque types are never outdated since they don't undergo type resolution, so nothing to do!
  16497             return Air.internedToRef(namespace.owner_type);
  16498         },
  16499         .struct_type, .union_type => {
  16500             const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
  16501             try sema.declareDependency(.{ .interned = new_ty });
  16502             return Air.internedToRef(new_ty);
  16503         },
  16504         .enum_type => {
  16505             const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
  16506             try sema.declareDependency(.{ .interned = new_ty });
  16507             // Since this is an enum, it has to be resolved immediately.
  16508             // `ensureTypeUpToDate` has resolved the new type if necessary.
  16509             // We just need to check for resolution failures.
  16510             const ty_unit: AnalUnit = .wrap(.{ .type = new_ty });
  16511             if (zcu.failed_analysis.contains(ty_unit) or zcu.transitive_failed_analysis.contains(ty_unit)) {
  16512                 return error.AnalysisFail;
  16513             }
  16514             return Air.internedToRef(new_ty);
  16515         },
  16516         else => unreachable,
  16517     }
  16518 }
  16519 
  16520 fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  16521     const pt = sema.pt;
  16522     const zcu = pt.zcu;
  16523     const ip = &zcu.intern_pool;
  16524     const captures = Type.fromInterned(zcu.namespacePtr(block.namespace).owner_type).getCaptures(zcu);
  16525 
  16526     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  16527     const src = block.nodeOffset(src_node);
  16528 
  16529     const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) {
  16530         .@"comptime" => |index| return Air.internedToRef(index),
  16531         .runtime => |index| index,
  16532         .nav_val => |nav| return sema.analyzeNavVal(block, src, nav),
  16533         .nav_ref => |nav| return sema.analyzeNavRef(block, src, nav),
  16534     };
  16535 
  16536     // The comptime case is handled already above. Runtime case below.
  16537 
  16538     if (!block.is_typeof and sema.func_index == .none) {
  16539         const msg = msg: {
  16540             const name = name: {
  16541                 // TODO: we should probably store this name in the ZIR to avoid this complexity.
  16542                 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
  16543                 const tree = file.getTree(zcu) catch |err| {
  16544                     // In this case we emit a warning + a less precise source location.
  16545                     log.warn("unable to load {f}: {s}", .{
  16546                         file.path.fmt(zcu.comp), @errorName(err),
  16547                     });
  16548                     break :name null;
  16549                 };
  16550                 const node = src_node.toAbsolute(src_base_node);
  16551                 const token = tree.nodeMainToken(node);
  16552                 break :name tree.tokenSlice(token);
  16553             };
  16554 
  16555             const msg = if (name) |some|
  16556                 try sema.errMsg(src, "'{s}' not accessible outside function scope", .{some})
  16557             else
  16558                 try sema.errMsg(src, "variable not accessible outside function scope", .{});
  16559             errdefer msg.destroy(sema.gpa);
  16560 
  16561             // TODO add "declared here" note
  16562             break :msg msg;
  16563         };
  16564         return sema.failWithOwnedErrorMsg(block, msg);
  16565     }
  16566 
  16567     if (!block.is_typeof and !block.isComptime() and sema.func_index != .none) {
  16568         const msg = msg: {
  16569             const name = name: {
  16570                 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
  16571                 const tree = file.getTree(zcu) catch |err| {
  16572                     // In this case we emit a warning + a less precise source location.
  16573                     log.warn("unable to load {f}: {s}", .{
  16574                         file.path.fmt(zcu.comp), @errorName(err),
  16575                     });
  16576                     break :name null;
  16577                 };
  16578                 const node = src_node.toAbsolute(src_base_node);
  16579                 const token = tree.nodeMainToken(node);
  16580                 break :name tree.tokenSlice(token);
  16581             };
  16582 
  16583             const msg = if (name) |some|
  16584                 try sema.errMsg(src, "'{s}' not accessible from inner function", .{some})
  16585             else
  16586                 try sema.errMsg(src, "variable not accessible from inner function", .{});
  16587             errdefer msg.destroy(sema.gpa);
  16588 
  16589             try sema.errNote(block.nodeOffset(.zero), msg, "crossed function definition here", .{});
  16590 
  16591             // TODO add "declared here" note
  16592             break :msg msg;
  16593         };
  16594         return sema.failWithOwnedErrorMsg(block, msg);
  16595     }
  16596 
  16597     assert(block.is_typeof);
  16598     // We need a dummy runtime instruction with the correct type.
  16599     return block.addTy(.alloc, .fromInterned(capture_ty));
  16600 }
  16601 
  16602 fn zirRetAddr(
  16603     sema: *Sema,
  16604     block: *Block,
  16605     extended: Zir.Inst.Extended.InstData,
  16606 ) CompileError!Air.Inst.Ref {
  16607     _ = sema;
  16608     _ = extended;
  16609     if (block.isComptime()) {
  16610         // TODO: we could give a meaningful lazy value here. #14938
  16611         return .zero_usize;
  16612     } else {
  16613         return block.addNoOp(.ret_addr);
  16614     }
  16615 }
  16616 
  16617 fn zirFrameAddress(
  16618     sema: *Sema,
  16619     block: *Block,
  16620     extended: Zir.Inst.Extended.InstData,
  16621 ) CompileError!Air.Inst.Ref {
  16622     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  16623     const src = block.nodeOffset(src_node);
  16624     try sema.requireRuntimeBlock(block, src, null);
  16625     return try block.addNoOp(.frame_addr);
  16626 }
  16627 
  16628 fn zirBuiltinSrc(
  16629     sema: *Sema,
  16630     block: *Block,
  16631     extended: Zir.Inst.Extended.InstData,
  16632 ) CompileError!Air.Inst.Ref {
  16633     const tracy = trace(@src());
  16634     defer tracy.end();
  16635 
  16636     const pt = sema.pt;
  16637     const zcu = pt.zcu;
  16638     const ip = &zcu.intern_pool;
  16639     const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data;
  16640     const fn_name = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).name;
  16641     const gpa = sema.gpa;
  16642     const file_scope = block.getFileScope(zcu);
  16643 
  16644     const func_name_val = v: {
  16645         const func_name_len = fn_name.length(ip);
  16646         const array_ty = try pt.intern(.{ .array_type = .{
  16647             .len = func_name_len,
  16648             .sentinel = .zero_u8,
  16649             .child = .u8_type,
  16650         } });
  16651         break :v try pt.intern(.{ .slice = .{
  16652             .ty = .slice_const_u8_sentinel_0_type,
  16653             .ptr = try pt.intern(.{ .ptr = .{
  16654                 .ty = .manyptr_const_u8_sentinel_0_type,
  16655                 .base_addr = .{ .uav = .{
  16656                     .orig_ty = .slice_const_u8_sentinel_0_type,
  16657                     .val = try pt.intern(.{ .aggregate = .{
  16658                         .ty = array_ty,
  16659                         .storage = .{ .bytes = fn_name.toString() },
  16660                     } }),
  16661                 } },
  16662                 .byte_offset = 0,
  16663             } }),
  16664             .len = (try pt.intValue(.usize, func_name_len)).toIntern(),
  16665         } });
  16666     };
  16667 
  16668     const module_name_val = v: {
  16669         const module_name = file_scope.mod.?.fully_qualified_name;
  16670         const array_ty = try pt.intern(.{ .array_type = .{
  16671             .len = module_name.len,
  16672             .sentinel = .zero_u8,
  16673             .child = .u8_type,
  16674         } });
  16675         break :v try pt.intern(.{ .slice = .{
  16676             .ty = .slice_const_u8_sentinel_0_type,
  16677             .ptr = try pt.intern(.{ .ptr = .{
  16678                 .ty = .manyptr_const_u8_sentinel_0_type,
  16679                 .base_addr = .{ .uav = .{
  16680                     .orig_ty = .slice_const_u8_sentinel_0_type,
  16681                     .val = try pt.intern(.{ .aggregate = .{
  16682                         .ty = array_ty,
  16683                         .storage = .{
  16684                             .bytes = try ip.getOrPutString(gpa, pt.tid, module_name, .maybe_embedded_nulls),
  16685                         },
  16686                     } }),
  16687                 } },
  16688                 .byte_offset = 0,
  16689             } }),
  16690             .len = (try pt.intValue(.usize, module_name.len)).toIntern(),
  16691         } });
  16692     };
  16693 
  16694     const file_name_val = v: {
  16695         const file_name = file_scope.sub_file_path;
  16696         const array_ty = try pt.intern(.{ .array_type = .{
  16697             .len = file_name.len,
  16698             .sentinel = .zero_u8,
  16699             .child = .u8_type,
  16700         } });
  16701         break :v try pt.intern(.{ .slice = .{
  16702             .ty = .slice_const_u8_sentinel_0_type,
  16703             .ptr = try pt.intern(.{ .ptr = .{
  16704                 .ty = .manyptr_const_u8_sentinel_0_type,
  16705                 .base_addr = .{ .uav = .{
  16706                     .orig_ty = .slice_const_u8_sentinel_0_type,
  16707                     .val = try pt.intern(.{ .aggregate = .{
  16708                         .ty = array_ty,
  16709                         .storage = .{
  16710                             .bytes = try ip.getOrPutString(gpa, pt.tid, file_name, .maybe_embedded_nulls),
  16711                         },
  16712                     } }),
  16713                 } },
  16714                 .byte_offset = 0,
  16715             } }),
  16716             .len = (try pt.intValue(.usize, file_name.len)).toIntern(),
  16717         } });
  16718     };
  16719 
  16720     const src_loc_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .SourceLocation);
  16721     const fields = .{
  16722         // module: [:0]const u8,
  16723         module_name_val,
  16724         // file: [:0]const u8,
  16725         file_name_val,
  16726         // fn_name: [:0]const u8,
  16727         func_name_val,
  16728         // line: u32,
  16729         (try pt.intValue(.u32, extra.line + 1)).toIntern(),
  16730         // column: u32,
  16731         (try pt.intValue(.u32, extra.column + 1)).toIntern(),
  16732     };
  16733     return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  16734         .ty = src_loc_ty.toIntern(),
  16735         .storage = .{ .elems = &fields },
  16736     } })));
  16737 }
  16738 
  16739 fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  16740     const pt = sema.pt;
  16741     const zcu = pt.zcu;
  16742     const gpa = sema.gpa;
  16743     const ip = &zcu.intern_pool;
  16744     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  16745     const src = block.nodeOffset(inst_data.src_node);
  16746     const ty = try sema.resolveType(block, src, inst_data.operand);
  16747     const type_info_ty = try sema.getBuiltinType(src, .Type);
  16748     const type_info_tag_ty = type_info_ty.unionTagType(zcu).?;
  16749 
  16750     if (ty.typeDeclInst(zcu)) |type_decl_inst| {
  16751         try sema.declareDependency(.{ .namespace = type_decl_inst });
  16752     }
  16753 
  16754     switch (ty.zigTypeTag(zcu)) {
  16755         .type,
  16756         .void,
  16757         .bool,
  16758         .noreturn,
  16759         .comptime_float,
  16760         .comptime_int,
  16761         .undefined,
  16762         .null,
  16763         .enum_literal,
  16764         => |type_info_tag| return unionInitFromEnumTag(sema, block, src, type_info_ty, @intFromEnum(type_info_tag), .void_value),
  16765 
  16766         .@"fn" => {
  16767             const fn_info_ty = try sema.getBuiltinType(src, .@"Type.Fn");
  16768             const param_info_ty = try sema.getBuiltinType(src, .@"Type.Fn.Param");
  16769 
  16770             const func_ty_info = zcu.typeToFunc(ty).?;
  16771             const param_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);
  16772             for (param_vals, 0..) |*param_val, i| {
  16773                 const param_ty = func_ty_info.param_types.get(ip)[i];
  16774                 const is_generic = param_ty == .generic_poison_type;
  16775                 const param_ty_val = try pt.intern(.{ .opt = .{
  16776                     .ty = try pt.intern(.{ .opt_type = .type_type }),
  16777                     .val = if (is_generic) .none else param_ty,
  16778                 } });
  16779 
  16780                 const is_noalias = blk: {
  16781                     const index = std.math.cast(u5, i) orelse break :blk false;
  16782                     break :blk @as(u1, @truncate(func_ty_info.noalias_bits >> index)) != 0;
  16783                 };
  16784 
  16785                 const param_fields = .{
  16786                     // is_generic: bool,
  16787                     Value.makeBool(is_generic).toIntern(),
  16788                     // is_noalias: bool,
  16789                     Value.makeBool(is_noalias).toIntern(),
  16790                     // type: ?type,
  16791                     param_ty_val,
  16792                 };
  16793                 param_val.* = try pt.intern(.{ .aggregate = .{
  16794                     .ty = param_info_ty.toIntern(),
  16795                     .storage = .{ .elems = &param_fields },
  16796                 } });
  16797             }
  16798 
  16799             const args_val = v: {
  16800                 const new_decl_ty = try pt.arrayType(.{
  16801                     .len = param_vals.len,
  16802                     .child = param_info_ty.toIntern(),
  16803                 });
  16804                 const new_decl_val = try pt.intern(.{ .aggregate = .{
  16805                     .ty = new_decl_ty.toIntern(),
  16806                     .storage = .{ .elems = param_vals },
  16807                 } });
  16808                 const slice_ty = (try pt.ptrTypeSema(.{
  16809                     .child = param_info_ty.toIntern(),
  16810                     .flags = .{
  16811                         .size = .slice,
  16812                         .is_const = true,
  16813                     },
  16814                 })).toIntern();
  16815                 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  16816                 break :v try pt.intern(.{ .slice = .{
  16817                     .ty = slice_ty,
  16818                     .ptr = try pt.intern(.{ .ptr = .{
  16819                         .ty = manyptr_ty,
  16820                         .base_addr = .{ .uav = .{
  16821                             .orig_ty = manyptr_ty,
  16822                             .val = new_decl_val,
  16823                         } },
  16824                         .byte_offset = 0,
  16825                     } }),
  16826                     .len = (try pt.intValue(.usize, param_vals.len)).toIntern(),
  16827                 } });
  16828             };
  16829 
  16830             const ret_ty_opt = try pt.intern(.{ .opt = .{
  16831                 .ty = try pt.intern(.{ .opt_type = .type_type }),
  16832                 .val = opt_val: {
  16833                     const ret_ty: Type = .fromInterned(func_ty_info.return_type);
  16834                     if (ret_ty.toIntern() == .generic_poison_type) break :opt_val .none;
  16835                     if (ret_ty.zigTypeTag(zcu) == .error_union) {
  16836                         if (ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) {
  16837                             break :opt_val .none;
  16838                         }
  16839                     }
  16840                     break :opt_val ret_ty.toIntern();
  16841                 },
  16842             } });
  16843 
  16844             const callconv_ty = try sema.getBuiltinType(src, .CallingConvention);
  16845             const callconv_val = Value.uninterpret(func_ty_info.cc, callconv_ty, pt) catch |err| switch (err) {
  16846                 error.TypeMismatch => @panic("std.builtin is corrupt"),
  16847                 error.OutOfMemory => |e| return e,
  16848             };
  16849 
  16850             const field_values: [5]InternPool.Index = .{
  16851                 // calling_convention: CallingConvention,
  16852                 callconv_val.toIntern(),
  16853                 // is_generic: bool,
  16854                 Value.makeBool(func_ty_info.is_generic).toIntern(),
  16855                 // is_var_args: bool,
  16856                 Value.makeBool(func_ty_info.is_var_args).toIntern(),
  16857                 // return_type: ?type,
  16858                 ret_ty_opt,
  16859                 // args: []const Fn.Param,
  16860                 args_val,
  16861             };
  16862             return Air.internedToRef((try pt.internUnion(.{
  16863                 .ty = type_info_ty.toIntern(),
  16864                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"fn"))).toIntern(),
  16865                 .val = try pt.intern(.{ .aggregate = .{
  16866                     .ty = fn_info_ty.toIntern(),
  16867                     .storage = .{ .elems = &field_values },
  16868                 } }),
  16869             })));
  16870         },
  16871         .int => {
  16872             const int_info_ty = try sema.getBuiltinType(src, .@"Type.Int");
  16873             const signedness_ty = try sema.getBuiltinType(src, .Signedness);
  16874             const info = ty.intInfo(zcu);
  16875             const field_values = .{
  16876                 // signedness: Signedness,
  16877                 (try pt.enumValueFieldIndex(signedness_ty, @intFromEnum(info.signedness))).toIntern(),
  16878                 // bits: u16,
  16879                 (try pt.intValue(.u16, info.bits)).toIntern(),
  16880             };
  16881             return Air.internedToRef((try pt.internUnion(.{
  16882                 .ty = type_info_ty.toIntern(),
  16883                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.int))).toIntern(),
  16884                 .val = try pt.intern(.{ .aggregate = .{
  16885                     .ty = int_info_ty.toIntern(),
  16886                     .storage = .{ .elems = &field_values },
  16887                 } }),
  16888             })));
  16889         },
  16890         .float => {
  16891             const float_info_ty = try sema.getBuiltinType(src, .@"Type.Float");
  16892 
  16893             const field_vals = .{
  16894                 // bits: u16,
  16895                 (try pt.intValue(.u16, ty.bitSize(zcu))).toIntern(),
  16896             };
  16897             return Air.internedToRef((try pt.internUnion(.{
  16898                 .ty = type_info_ty.toIntern(),
  16899                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.float))).toIntern(),
  16900                 .val = try pt.intern(.{ .aggregate = .{
  16901                     .ty = float_info_ty.toIntern(),
  16902                     .storage = .{ .elems = &field_vals },
  16903                 } }),
  16904             })));
  16905         },
  16906         .pointer => {
  16907             const info = ty.ptrInfo(zcu);
  16908             const alignment = if (info.flags.alignment.toByteUnits()) |alignment|
  16909                 try pt.intValue(.comptime_int, alignment)
  16910             else
  16911                 try Type.fromInterned(info.child).lazyAbiAlignment(pt);
  16912 
  16913             const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace);
  16914             const pointer_ty = try sema.getBuiltinType(src, .@"Type.Pointer");
  16915             const ptr_size_ty = try sema.getBuiltinType(src, .@"Type.Pointer.Size");
  16916 
  16917             const field_values = .{
  16918                 // size: Size,
  16919                 (try pt.enumValueFieldIndex(ptr_size_ty, @intFromEnum(info.flags.size))).toIntern(),
  16920                 // is_const: bool,
  16921                 Value.makeBool(info.flags.is_const).toIntern(),
  16922                 // is_volatile: bool,
  16923                 Value.makeBool(info.flags.is_volatile).toIntern(),
  16924                 // alignment: comptime_int,
  16925                 alignment.toIntern(),
  16926                 // address_space: AddressSpace
  16927                 (try pt.enumValueFieldIndex(addrspace_ty, @intFromEnum(info.flags.address_space))).toIntern(),
  16928                 // child: type,
  16929                 info.child,
  16930                 // is_allowzero: bool,
  16931                 Value.makeBool(info.flags.is_allowzero).toIntern(),
  16932                 // sentinel: ?*const anyopaque,
  16933                 (try sema.optRefValue(switch (info.sentinel) {
  16934                     .none => null,
  16935                     else => Value.fromInterned(info.sentinel),
  16936                 })).toIntern(),
  16937             };
  16938             return Air.internedToRef((try pt.internUnion(.{
  16939                 .ty = type_info_ty.toIntern(),
  16940                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.pointer))).toIntern(),
  16941                 .val = try pt.intern(.{ .aggregate = .{
  16942                     .ty = pointer_ty.toIntern(),
  16943                     .storage = .{ .elems = &field_values },
  16944                 } }),
  16945             })));
  16946         },
  16947         .array => {
  16948             const array_field_ty = try sema.getBuiltinType(src, .@"Type.Array");
  16949 
  16950             const info = ty.arrayInfo(zcu);
  16951             const field_values = .{
  16952                 // len: comptime_int,
  16953                 (try pt.intValue(.comptime_int, info.len)).toIntern(),
  16954                 // child: type,
  16955                 info.elem_type.toIntern(),
  16956                 // sentinel: ?*const anyopaque,
  16957                 (try sema.optRefValue(info.sentinel)).toIntern(),
  16958             };
  16959             return Air.internedToRef((try pt.internUnion(.{
  16960                 .ty = type_info_ty.toIntern(),
  16961                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.array))).toIntern(),
  16962                 .val = try pt.intern(.{ .aggregate = .{
  16963                     .ty = array_field_ty.toIntern(),
  16964                     .storage = .{ .elems = &field_values },
  16965                 } }),
  16966             })));
  16967         },
  16968         .vector => {
  16969             const vector_field_ty = try sema.getBuiltinType(src, .@"Type.Vector");
  16970 
  16971             const info = ty.arrayInfo(zcu);
  16972             const field_values = .{
  16973                 // len: comptime_int,
  16974                 (try pt.intValue(.comptime_int, info.len)).toIntern(),
  16975                 // child: type,
  16976                 info.elem_type.toIntern(),
  16977             };
  16978             return Air.internedToRef((try pt.internUnion(.{
  16979                 .ty = type_info_ty.toIntern(),
  16980                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.vector))).toIntern(),
  16981                 .val = try pt.intern(.{ .aggregate = .{
  16982                     .ty = vector_field_ty.toIntern(),
  16983                     .storage = .{ .elems = &field_values },
  16984                 } }),
  16985             })));
  16986         },
  16987         .optional => {
  16988             const optional_field_ty = try sema.getBuiltinType(src, .@"Type.Optional");
  16989 
  16990             const field_values = .{
  16991                 // child: type,
  16992                 ty.optionalChild(zcu).toIntern(),
  16993             };
  16994             return Air.internedToRef((try pt.internUnion(.{
  16995                 .ty = type_info_ty.toIntern(),
  16996                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.optional))).toIntern(),
  16997                 .val = try pt.intern(.{ .aggregate = .{
  16998                     .ty = optional_field_ty.toIntern(),
  16999                     .storage = .{ .elems = &field_values },
  17000                 } }),
  17001             })));
  17002         },
  17003         .error_set => {
  17004             // Get the Error type
  17005             const error_field_ty = try sema.getBuiltinType(src, .@"Type.Error");
  17006 
  17007             // Build our list of Error values
  17008             // Optional value is only null if anyerror
  17009             // Value can be zero-length slice otherwise
  17010             const error_field_vals = switch (try sema.resolveInferredErrorSetTy(block, src, ty.toIntern())) {
  17011                 .anyerror_type => null,
  17012                 else => |err_set_ty_index| blk: {
  17013                     const names = ip.indexToKey(err_set_ty_index).error_set_type.names;
  17014                     const vals = try sema.arena.alloc(InternPool.Index, names.len);
  17015                     for (vals, 0..) |*field_val, error_index| {
  17016                         const error_name = names.get(ip)[error_index];
  17017                         const error_name_len = error_name.length(ip);
  17018                         const error_name_val = v: {
  17019                             const new_decl_ty = try pt.arrayType(.{
  17020                                 .len = error_name_len,
  17021                                 .sentinel = .zero_u8,
  17022                                 .child = .u8_type,
  17023                             });
  17024                             const new_decl_val = try pt.intern(.{ .aggregate = .{
  17025                                 .ty = new_decl_ty.toIntern(),
  17026                                 .storage = .{ .bytes = error_name.toString() },
  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                                         .val = new_decl_val,
  17034                                         .orig_ty = .slice_const_u8_sentinel_0_type,
  17035                                     } },
  17036                                     .byte_offset = 0,
  17037                                 } }),
  17038                                 .len = (try pt.intValue(.usize, error_name_len)).toIntern(),
  17039                             } });
  17040                         };
  17041 
  17042                         const error_field_fields = .{
  17043                             // name: [:0]const u8,
  17044                             error_name_val,
  17045                         };
  17046                         field_val.* = try pt.intern(.{ .aggregate = .{
  17047                             .ty = error_field_ty.toIntern(),
  17048                             .storage = .{ .elems = &error_field_fields },
  17049                         } });
  17050                     }
  17051 
  17052                     break :blk vals;
  17053                 },
  17054             };
  17055 
  17056             // Build our ?[]const Error value
  17057             const slice_errors_ty = try pt.ptrTypeSema(.{
  17058                 .child = error_field_ty.toIntern(),
  17059                 .flags = .{
  17060                     .size = .slice,
  17061                     .is_const = true,
  17062                 },
  17063             });
  17064             const opt_slice_errors_ty = try pt.optionalType(slice_errors_ty.toIntern());
  17065             const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: {
  17066                 const array_errors_ty = try pt.arrayType(.{
  17067                     .len = vals.len,
  17068                     .child = error_field_ty.toIntern(),
  17069                 });
  17070                 const new_decl_val = try pt.intern(.{ .aggregate = .{
  17071                     .ty = array_errors_ty.toIntern(),
  17072                     .storage = .{ .elems = vals },
  17073                 } });
  17074                 const manyptr_errors_ty = slice_errors_ty.slicePtrFieldType(zcu).toIntern();
  17075                 break :v try pt.intern(.{ .slice = .{
  17076                     .ty = slice_errors_ty.toIntern(),
  17077                     .ptr = try pt.intern(.{ .ptr = .{
  17078                         .ty = manyptr_errors_ty,
  17079                         .base_addr = .{ .uav = .{
  17080                             .orig_ty = manyptr_errors_ty,
  17081                             .val = new_decl_val,
  17082                         } },
  17083                         .byte_offset = 0,
  17084                     } }),
  17085                     .len = (try pt.intValue(.usize, vals.len)).toIntern(),
  17086                 } });
  17087             } else .none;
  17088             const errors_val = try pt.intern(.{ .opt = .{
  17089                 .ty = opt_slice_errors_ty.toIntern(),
  17090                 .val = errors_payload_val,
  17091             } });
  17092 
  17093             // Construct Type{ .error_set = errors_val }
  17094             return Air.internedToRef((try pt.internUnion(.{
  17095                 .ty = type_info_ty.toIntern(),
  17096                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.error_set))).toIntern(),
  17097                 .val = errors_val,
  17098             })));
  17099         },
  17100         .error_union => {
  17101             const error_union_field_ty = try sema.getBuiltinType(src, .@"Type.ErrorUnion");
  17102 
  17103             const field_values = .{
  17104                 // error_set: type,
  17105                 ty.errorUnionSet(zcu).toIntern(),
  17106                 // payload: type,
  17107                 ty.errorUnionPayload(zcu).toIntern(),
  17108             };
  17109             return Air.internedToRef((try pt.internUnion(.{
  17110                 .ty = type_info_ty.toIntern(),
  17111                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.error_union))).toIntern(),
  17112                 .val = try pt.intern(.{ .aggregate = .{
  17113                     .ty = error_union_field_ty.toIntern(),
  17114                     .storage = .{ .elems = &field_values },
  17115                 } }),
  17116             })));
  17117         },
  17118         .@"enum" => {
  17119             const is_exhaustive = Value.makeBool(ip.loadEnumType(ty.toIntern()).tag_mode != .nonexhaustive);
  17120 
  17121             const enum_field_ty = try sema.getBuiltinType(src, .@"Type.EnumField");
  17122 
  17123             const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.loadEnumType(ty.toIntern()).names.len);
  17124             for (enum_field_vals, 0..) |*field_val, tag_index| {
  17125                 const enum_type = ip.loadEnumType(ty.toIntern());
  17126                 const value_val = if (enum_type.values.len > 0)
  17127                     try ip.getCoercedInts(
  17128                         zcu.gpa,
  17129                         pt.tid,
  17130                         ip.indexToKey(enum_type.values.get(ip)[tag_index]).int,
  17131                         .comptime_int_type,
  17132                     )
  17133                 else
  17134                     (try pt.intValue(.comptime_int, tag_index)).toIntern();
  17135 
  17136                 // TODO: write something like getCoercedInts to avoid needing to dupe
  17137                 const name_val = v: {
  17138                     const tag_name = enum_type.names.get(ip)[tag_index];
  17139                     const tag_name_len = tag_name.length(ip);
  17140                     const new_decl_ty = try pt.arrayType(.{
  17141                         .len = tag_name_len,
  17142                         .sentinel = .zero_u8,
  17143                         .child = .u8_type,
  17144                     });
  17145                     const new_decl_val = try pt.intern(.{ .aggregate = .{
  17146                         .ty = new_decl_ty.toIntern(),
  17147                         .storage = .{ .bytes = tag_name.toString() },
  17148                     } });
  17149                     break :v try pt.intern(.{ .slice = .{
  17150                         .ty = .slice_const_u8_sentinel_0_type,
  17151                         .ptr = try pt.intern(.{ .ptr = .{
  17152                             .ty = .manyptr_const_u8_sentinel_0_type,
  17153                             .base_addr = .{ .uav = .{
  17154                                 .val = new_decl_val,
  17155                                 .orig_ty = .slice_const_u8_sentinel_0_type,
  17156                             } },
  17157                             .byte_offset = 0,
  17158                         } }),
  17159                         .len = (try pt.intValue(.usize, tag_name_len)).toIntern(),
  17160                     } });
  17161                 };
  17162 
  17163                 const enum_field_fields = .{
  17164                     // name: [:0]const u8,
  17165                     name_val,
  17166                     // value: comptime_int,
  17167                     value_val,
  17168                 };
  17169                 field_val.* = try pt.intern(.{ .aggregate = .{
  17170                     .ty = enum_field_ty.toIntern(),
  17171                     .storage = .{ .elems = &enum_field_fields },
  17172                 } });
  17173             }
  17174 
  17175             const fields_val = v: {
  17176                 const fields_array_ty = try pt.arrayType(.{
  17177                     .len = enum_field_vals.len,
  17178                     .child = enum_field_ty.toIntern(),
  17179                 });
  17180                 const new_decl_val = try pt.intern(.{ .aggregate = .{
  17181                     .ty = fields_array_ty.toIntern(),
  17182                     .storage = .{ .elems = enum_field_vals },
  17183                 } });
  17184                 const slice_ty = (try pt.ptrTypeSema(.{
  17185                     .child = enum_field_ty.toIntern(),
  17186                     .flags = .{
  17187                         .size = .slice,
  17188                         .is_const = true,
  17189                     },
  17190                 })).toIntern();
  17191                 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  17192                 break :v try pt.intern(.{ .slice = .{
  17193                     .ty = slice_ty,
  17194                     .ptr = try pt.intern(.{ .ptr = .{
  17195                         .ty = manyptr_ty,
  17196                         .base_addr = .{ .uav = .{
  17197                             .val = new_decl_val,
  17198                             .orig_ty = manyptr_ty,
  17199                         } },
  17200                         .byte_offset = 0,
  17201                     } }),
  17202                     .len = (try pt.intValue(.usize, enum_field_vals.len)).toIntern(),
  17203                 } });
  17204             };
  17205 
  17206             const decls_val = try sema.typeInfoDecls(src, ip.loadEnumType(ty.toIntern()).namespace.toOptional());
  17207 
  17208             const type_enum_ty = try sema.getBuiltinType(src, .@"Type.Enum");
  17209 
  17210             const field_values = .{
  17211                 // tag_type: type,
  17212                 ip.loadEnumType(ty.toIntern()).tag_ty,
  17213                 // fields: []const EnumField,
  17214                 fields_val,
  17215                 // decls: []const Declaration,
  17216                 decls_val,
  17217                 // is_exhaustive: bool,
  17218                 is_exhaustive.toIntern(),
  17219             };
  17220             return Air.internedToRef((try pt.internUnion(.{
  17221                 .ty = type_info_ty.toIntern(),
  17222                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"enum"))).toIntern(),
  17223                 .val = try pt.intern(.{ .aggregate = .{
  17224                     .ty = type_enum_ty.toIntern(),
  17225                     .storage = .{ .elems = &field_values },
  17226                 } }),
  17227             })));
  17228         },
  17229         .@"union" => {
  17230             const type_union_ty = try sema.getBuiltinType(src, .@"Type.Union");
  17231             const union_field_ty = try sema.getBuiltinType(src, .@"Type.UnionField");
  17232 
  17233             try ty.resolveLayout(pt); // Getting alignment requires type layout
  17234             const union_obj = zcu.typeToUnion(ty).?;
  17235             const tag_type = union_obj.loadTagType(ip);
  17236             const layout = union_obj.flagsUnordered(ip).layout;
  17237 
  17238             const union_field_vals = try gpa.alloc(InternPool.Index, tag_type.names.len);
  17239             defer gpa.free(union_field_vals);
  17240 
  17241             for (union_field_vals, 0..) |*field_val, field_index| {
  17242                 const name_val = v: {
  17243                     const field_name = tag_type.names.get(ip)[field_index];
  17244                     const field_name_len = field_name.length(ip);
  17245                     const new_decl_ty = try pt.arrayType(.{
  17246                         .len = field_name_len,
  17247                         .sentinel = .zero_u8,
  17248                         .child = .u8_type,
  17249                     });
  17250                     const new_decl_val = try pt.intern(.{ .aggregate = .{
  17251                         .ty = new_decl_ty.toIntern(),
  17252                         .storage = .{ .bytes = field_name.toString() },
  17253                     } });
  17254                     break :v try pt.intern(.{ .slice = .{
  17255                         .ty = .slice_const_u8_sentinel_0_type,
  17256                         .ptr = try pt.intern(.{ .ptr = .{
  17257                             .ty = .manyptr_const_u8_sentinel_0_type,
  17258                             .base_addr = .{ .uav = .{
  17259                                 .val = new_decl_val,
  17260                                 .orig_ty = .slice_const_u8_sentinel_0_type,
  17261                             } },
  17262                             .byte_offset = 0,
  17263                         } }),
  17264                         .len = (try pt.intValue(.usize, field_name_len)).toIntern(),
  17265                     } });
  17266                 };
  17267 
  17268                 const alignment = switch (layout) {
  17269                     .auto, .@"extern" => try ty.fieldAlignmentSema(field_index, pt),
  17270                     .@"packed" => .none,
  17271                 };
  17272 
  17273                 const field_ty = union_obj.field_types.get(ip)[field_index];
  17274                 const union_field_fields = .{
  17275                     // name: [:0]const u8,
  17276                     name_val,
  17277                     // type: type,
  17278                     field_ty,
  17279                     // alignment: comptime_int,
  17280                     (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(),
  17281                 };
  17282                 field_val.* = try pt.intern(.{ .aggregate = .{
  17283                     .ty = union_field_ty.toIntern(),
  17284                     .storage = .{ .elems = &union_field_fields },
  17285                 } });
  17286             }
  17287 
  17288             const fields_val = v: {
  17289                 const array_fields_ty = try pt.arrayType(.{
  17290                     .len = union_field_vals.len,
  17291                     .child = union_field_ty.toIntern(),
  17292                 });
  17293                 const new_decl_val = try pt.intern(.{ .aggregate = .{
  17294                     .ty = array_fields_ty.toIntern(),
  17295                     .storage = .{ .elems = union_field_vals },
  17296                 } });
  17297                 const slice_ty = (try pt.ptrTypeSema(.{
  17298                     .child = union_field_ty.toIntern(),
  17299                     .flags = .{
  17300                         .size = .slice,
  17301                         .is_const = true,
  17302                     },
  17303                 })).toIntern();
  17304                 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  17305                 break :v try pt.intern(.{ .slice = .{
  17306                     .ty = slice_ty,
  17307                     .ptr = try pt.intern(.{ .ptr = .{
  17308                         .ty = manyptr_ty,
  17309                         .base_addr = .{ .uav = .{
  17310                             .orig_ty = manyptr_ty,
  17311                             .val = new_decl_val,
  17312                         } },
  17313                         .byte_offset = 0,
  17314                     } }),
  17315                     .len = (try pt.intValue(.usize, union_field_vals.len)).toIntern(),
  17316                 } });
  17317             };
  17318 
  17319             const decls_val = try sema.typeInfoDecls(src, ty.getNamespaceIndex(zcu).toOptional());
  17320 
  17321             const enum_tag_ty_val = try pt.intern(.{ .opt = .{
  17322                 .ty = (try pt.optionalType(.type_type)).toIntern(),
  17323                 .val = if (ty.unionTagType(zcu)) |tag_ty| tag_ty.toIntern() else .none,
  17324             } });
  17325 
  17326             const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout");
  17327 
  17328             const field_values = .{
  17329                 // layout: ContainerLayout,
  17330                 (try pt.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(),
  17331 
  17332                 // tag_type: ?type,
  17333                 enum_tag_ty_val,
  17334                 // fields: []const UnionField,
  17335                 fields_val,
  17336                 // decls: []const Declaration,
  17337                 decls_val,
  17338             };
  17339             return Air.internedToRef((try pt.internUnion(.{
  17340                 .ty = type_info_ty.toIntern(),
  17341                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"union"))).toIntern(),
  17342                 .val = try pt.intern(.{ .aggregate = .{
  17343                     .ty = type_union_ty.toIntern(),
  17344                     .storage = .{ .elems = &field_values },
  17345                 } }),
  17346             })));
  17347         },
  17348         .@"struct" => {
  17349             const type_struct_ty = try sema.getBuiltinType(src, .@"Type.Struct");
  17350             const struct_field_ty = try sema.getBuiltinType(src, .@"Type.StructField");
  17351 
  17352             try ty.resolveLayout(pt); // Getting alignment requires type layout
  17353 
  17354             var struct_field_vals: []InternPool.Index = &.{};
  17355             defer gpa.free(struct_field_vals);
  17356             fv: {
  17357                 const struct_type = switch (ip.indexToKey(ty.toIntern())) {
  17358                     .tuple_type => |tuple_type| {
  17359                         struct_field_vals = try gpa.alloc(InternPool.Index, tuple_type.types.len);
  17360                         for (struct_field_vals, 0..) |*struct_field_val, field_index| {
  17361                             const field_ty = tuple_type.types.get(ip)[field_index];
  17362                             const field_val = tuple_type.values.get(ip)[field_index];
  17363                             const name_val = v: {
  17364                                 const field_name = try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
  17365                                 const field_name_len = field_name.length(ip);
  17366                                 const new_decl_ty = try pt.arrayType(.{
  17367                                     .len = field_name_len,
  17368                                     .sentinel = .zero_u8,
  17369                                     .child = .u8_type,
  17370                                 });
  17371                                 const new_decl_val = try pt.intern(.{ .aggregate = .{
  17372                                     .ty = new_decl_ty.toIntern(),
  17373                                     .storage = .{ .bytes = field_name.toString() },
  17374                                 } });
  17375                                 break :v try pt.intern(.{ .slice = .{
  17376                                     .ty = .slice_const_u8_sentinel_0_type,
  17377                                     .ptr = try pt.intern(.{ .ptr = .{
  17378                                         .ty = .manyptr_const_u8_sentinel_0_type,
  17379                                         .base_addr = .{ .uav = .{
  17380                                             .val = new_decl_val,
  17381                                             .orig_ty = .slice_const_u8_sentinel_0_type,
  17382                                         } },
  17383                                         .byte_offset = 0,
  17384                                     } }),
  17385                                     .len = (try pt.intValue(.usize, field_name_len)).toIntern(),
  17386                                 } });
  17387                             };
  17388 
  17389                             try Type.fromInterned(field_ty).resolveLayout(pt);
  17390 
  17391                             const is_comptime = field_val != .none;
  17392                             const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null;
  17393                             const default_val_ptr = try sema.optRefValue(opt_default_val);
  17394                             const struct_field_fields = .{
  17395                                 // name: [:0]const u8,
  17396                                 name_val,
  17397                                 // type: type,
  17398                                 field_ty,
  17399                                 // default_value: ?*const anyopaque,
  17400                                 default_val_ptr.toIntern(),
  17401                                 // is_comptime: bool,
  17402                                 Value.makeBool(is_comptime).toIntern(),
  17403                                 // alignment: comptime_int,
  17404                                 (try pt.intValue(.comptime_int, Type.fromInterned(field_ty).abiAlignment(zcu).toByteUnits() orelse 0)).toIntern(),
  17405                             };
  17406                             struct_field_val.* = try pt.intern(.{ .aggregate = .{
  17407                                 .ty = struct_field_ty.toIntern(),
  17408                                 .storage = .{ .elems = &struct_field_fields },
  17409                             } });
  17410                         }
  17411                         break :fv;
  17412                     },
  17413                     .struct_type => ip.loadStructType(ty.toIntern()),
  17414                     else => unreachable,
  17415                 };
  17416                 struct_field_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
  17417 
  17418                 try ty.resolveStructFieldInits(pt);
  17419 
  17420                 for (struct_field_vals, 0..) |*field_val, field_index| {
  17421                     const field_name = if (struct_type.fieldName(ip, field_index).unwrap()) |field_name|
  17422                         field_name
  17423                     else
  17424                         try ip.getOrPutStringFmt(gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
  17425                     const field_name_len = field_name.length(ip);
  17426                     const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
  17427                     const field_init = struct_type.fieldInit(ip, field_index);
  17428                     const field_is_comptime = struct_type.fieldIsComptime(ip, field_index);
  17429                     const name_val = v: {
  17430                         const new_decl_ty = try pt.arrayType(.{
  17431                             .len = field_name_len,
  17432                             .sentinel = .zero_u8,
  17433                             .child = .u8_type,
  17434                         });
  17435                         const new_decl_val = try pt.intern(.{ .aggregate = .{
  17436                             .ty = new_decl_ty.toIntern(),
  17437                             .storage = .{ .bytes = field_name.toString() },
  17438                         } });
  17439                         break :v try pt.intern(.{ .slice = .{
  17440                             .ty = .slice_const_u8_sentinel_0_type,
  17441                             .ptr = try pt.intern(.{ .ptr = .{
  17442                                 .ty = .manyptr_const_u8_sentinel_0_type,
  17443                                 .base_addr = .{ .uav = .{
  17444                                     .val = new_decl_val,
  17445                                     .orig_ty = .slice_const_u8_sentinel_0_type,
  17446                                 } },
  17447                                 .byte_offset = 0,
  17448                             } }),
  17449                             .len = (try pt.intValue(.usize, field_name_len)).toIntern(),
  17450                         } });
  17451                     };
  17452 
  17453                     const opt_default_val = if (field_init == .none) null else Value.fromInterned(field_init);
  17454                     const default_val_ptr = try sema.optRefValue(opt_default_val);
  17455                     const alignment = switch (struct_type.layout) {
  17456                         .@"packed" => .none,
  17457                         else => try field_ty.structFieldAlignmentSema(
  17458                             struct_type.fieldAlign(ip, field_index),
  17459                             struct_type.layout,
  17460                             pt,
  17461                         ),
  17462                     };
  17463 
  17464                     const struct_field_fields = .{
  17465                         // name: [:0]const u8,
  17466                         name_val,
  17467                         // type: type,
  17468                         field_ty.toIntern(),
  17469                         // default_value: ?*const anyopaque,
  17470                         default_val_ptr.toIntern(),
  17471                         // is_comptime: bool,
  17472                         Value.makeBool(field_is_comptime).toIntern(),
  17473                         // alignment: comptime_int,
  17474                         (try pt.intValue(.comptime_int, alignment.toByteUnits() orelse 0)).toIntern(),
  17475                     };
  17476                     field_val.* = try pt.intern(.{ .aggregate = .{
  17477                         .ty = struct_field_ty.toIntern(),
  17478                         .storage = .{ .elems = &struct_field_fields },
  17479                     } });
  17480                 }
  17481             }
  17482 
  17483             const fields_val = v: {
  17484                 const array_fields_ty = try pt.arrayType(.{
  17485                     .len = struct_field_vals.len,
  17486                     .child = struct_field_ty.toIntern(),
  17487                 });
  17488                 const new_decl_val = try pt.intern(.{ .aggregate = .{
  17489                     .ty = array_fields_ty.toIntern(),
  17490                     .storage = .{ .elems = struct_field_vals },
  17491                 } });
  17492                 const slice_ty = (try pt.ptrTypeSema(.{
  17493                     .child = struct_field_ty.toIntern(),
  17494                     .flags = .{
  17495                         .size = .slice,
  17496                         .is_const = true,
  17497                     },
  17498                 })).toIntern();
  17499                 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  17500                 break :v try pt.intern(.{ .slice = .{
  17501                     .ty = slice_ty,
  17502                     .ptr = try pt.intern(.{ .ptr = .{
  17503                         .ty = manyptr_ty,
  17504                         .base_addr = .{ .uav = .{
  17505                             .orig_ty = manyptr_ty,
  17506                             .val = new_decl_val,
  17507                         } },
  17508                         .byte_offset = 0,
  17509                     } }),
  17510                     .len = (try pt.intValue(.usize, struct_field_vals.len)).toIntern(),
  17511                 } });
  17512             };
  17513 
  17514             const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu));
  17515 
  17516             const backing_integer_val = try pt.intern(.{ .opt = .{
  17517                 .ty = (try pt.optionalType(.type_type)).toIntern(),
  17518                 .val = if (zcu.typeToPackedStruct(ty)) |packed_struct| val: {
  17519                     assert(Type.fromInterned(packed_struct.backingIntTypeUnordered(ip)).isInt(zcu));
  17520                     break :val packed_struct.backingIntTypeUnordered(ip);
  17521                 } else .none,
  17522             } });
  17523 
  17524             const container_layout_ty = try sema.getBuiltinType(src, .@"Type.ContainerLayout");
  17525 
  17526             const layout = ty.containerLayout(zcu);
  17527 
  17528             const field_values = [_]InternPool.Index{
  17529                 // layout: ContainerLayout,
  17530                 (try pt.enumValueFieldIndex(container_layout_ty, @intFromEnum(layout))).toIntern(),
  17531                 // backing_integer: ?type,
  17532                 backing_integer_val,
  17533                 // fields: []const StructField,
  17534                 fields_val,
  17535                 // decls: []const Declaration,
  17536                 decls_val,
  17537                 // is_tuple: bool,
  17538                 Value.makeBool(ty.isTuple(zcu)).toIntern(),
  17539             };
  17540             return Air.internedToRef((try pt.internUnion(.{
  17541                 .ty = type_info_ty.toIntern(),
  17542                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"struct"))).toIntern(),
  17543                 .val = try pt.intern(.{ .aggregate = .{
  17544                     .ty = type_struct_ty.toIntern(),
  17545                     .storage = .{ .elems = &field_values },
  17546                 } }),
  17547             })));
  17548         },
  17549         .@"opaque" => {
  17550             const type_opaque_ty = try sema.getBuiltinType(src, .@"Type.Opaque");
  17551 
  17552             try ty.resolveFields(pt);
  17553             const decls_val = try sema.typeInfoDecls(src, ty.getNamespace(zcu));
  17554 
  17555             const field_values = .{
  17556                 // decls: []const Declaration,
  17557                 decls_val,
  17558             };
  17559             return Air.internedToRef((try pt.internUnion(.{
  17560                 .ty = type_info_ty.toIntern(),
  17561                 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @intFromEnum(std.builtin.TypeId.@"opaque"))).toIntern(),
  17562                 .val = try pt.intern(.{ .aggregate = .{
  17563                     .ty = type_opaque_ty.toIntern(),
  17564                     .storage = .{ .elems = &field_values },
  17565                 } }),
  17566             })));
  17567         },
  17568         .frame => return sema.failWithUseOfAsync(block, src),
  17569         .@"anyframe" => return sema.failWithUseOfAsync(block, src),
  17570     }
  17571 }
  17572 
  17573 fn typeInfoDecls(
  17574     sema: *Sema,
  17575     src: LazySrcLoc,
  17576     opt_namespace: InternPool.OptionalNamespaceIndex,
  17577 ) CompileError!InternPool.Index {
  17578     const pt = sema.pt;
  17579     const zcu = pt.zcu;
  17580     const gpa = sema.gpa;
  17581 
  17582     const declaration_ty = try sema.getBuiltinType(src, .@"Type.Declaration");
  17583 
  17584     var decl_vals = std.ArrayList(InternPool.Index).init(gpa);
  17585     defer decl_vals.deinit();
  17586 
  17587     var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa);
  17588     defer seen_namespaces.deinit();
  17589 
  17590     try sema.typeInfoNamespaceDecls(opt_namespace, declaration_ty, &decl_vals, &seen_namespaces);
  17591 
  17592     const array_decl_ty = try pt.arrayType(.{
  17593         .len = decl_vals.items.len,
  17594         .child = declaration_ty.toIntern(),
  17595     });
  17596     const new_decl_val = try pt.intern(.{ .aggregate = .{
  17597         .ty = array_decl_ty.toIntern(),
  17598         .storage = .{ .elems = decl_vals.items },
  17599     } });
  17600     const slice_ty = (try pt.ptrTypeSema(.{
  17601         .child = declaration_ty.toIntern(),
  17602         .flags = .{
  17603             .size = .slice,
  17604             .is_const = true,
  17605         },
  17606     })).toIntern();
  17607     const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
  17608     return try pt.intern(.{ .slice = .{
  17609         .ty = slice_ty,
  17610         .ptr = try pt.intern(.{ .ptr = .{
  17611             .ty = manyptr_ty,
  17612             .base_addr = .{ .uav = .{
  17613                 .orig_ty = manyptr_ty,
  17614                 .val = new_decl_val,
  17615             } },
  17616             .byte_offset = 0,
  17617         } }),
  17618         .len = (try pt.intValue(.usize, decl_vals.items.len)).toIntern(),
  17619     } });
  17620 }
  17621 
  17622 fn typeInfoNamespaceDecls(
  17623     sema: *Sema,
  17624     opt_namespace_index: InternPool.OptionalNamespaceIndex,
  17625     declaration_ty: Type,
  17626     decl_vals: *std.ArrayList(InternPool.Index),
  17627     seen_namespaces: *std.AutoHashMap(*Namespace, void),
  17628 ) !void {
  17629     const pt = sema.pt;
  17630     const zcu = pt.zcu;
  17631     const ip = &zcu.intern_pool;
  17632 
  17633     const namespace_index = opt_namespace_index.unwrap() orelse return;
  17634     try pt.ensureNamespaceUpToDate(namespace_index);
  17635     const namespace = zcu.namespacePtr(namespace_index);
  17636 
  17637     const gop = try seen_namespaces.getOrPut(namespace);
  17638     if (gop.found_existing) return;
  17639 
  17640     for (namespace.pub_decls.keys()) |nav| {
  17641         const name = ip.getNav(nav).name;
  17642         const name_val = name_val: {
  17643             const name_len = name.length(ip);
  17644             const array_ty = try pt.arrayType(.{
  17645                 .len = name_len,
  17646                 .sentinel = .zero_u8,
  17647                 .child = .u8_type,
  17648             });
  17649             const array_val = try pt.intern(.{ .aggregate = .{
  17650                 .ty = array_ty.toIntern(),
  17651                 .storage = .{ .bytes = name.toString() },
  17652             } });
  17653             break :name_val try pt.intern(.{
  17654                 .slice = .{
  17655                     .ty = .slice_const_u8_sentinel_0_type, // [:0]const u8
  17656                     .ptr = try pt.intern(.{
  17657                         .ptr = .{
  17658                             .ty = .manyptr_const_u8_sentinel_0_type, // [*:0]const u8
  17659                             .base_addr = .{ .uav = .{
  17660                                 .orig_ty = .slice_const_u8_sentinel_0_type,
  17661                                 .val = array_val,
  17662                             } },
  17663                             .byte_offset = 0,
  17664                         },
  17665                     }),
  17666                     .len = (try pt.intValue(.usize, name_len)).toIntern(),
  17667                 },
  17668             });
  17669         };
  17670         const fields = [_]InternPool.Index{
  17671             // name: [:0]const u8,
  17672             name_val,
  17673         };
  17674         try decl_vals.append(try pt.intern(.{ .aggregate = .{
  17675             .ty = declaration_ty.toIntern(),
  17676             .storage = .{ .elems = &fields },
  17677         } }));
  17678     }
  17679 }
  17680 
  17681 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17682     _ = block;
  17683     const zir_datas = sema.code.instructions.items(.data);
  17684     const inst_data = zir_datas[@intFromEnum(inst)].un_node;
  17685     const operand = try sema.resolveInst(inst_data.operand);
  17686     const operand_ty = sema.typeOf(operand);
  17687     return Air.internedToRef(operand_ty.toIntern());
  17688 }
  17689 
  17690 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17691     const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  17692     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
  17693     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  17694 
  17695     var child_block: Block = .{
  17696         .parent = block,
  17697         .sema = sema,
  17698         .namespace = block.namespace,
  17699         .instructions = .{},
  17700         .inlining = block.inlining,
  17701         .comptime_reason = null,
  17702         .is_typeof = true,
  17703         .want_safety = false,
  17704         .error_return_trace_index = block.error_return_trace_index,
  17705         .src_base_inst = block.src_base_inst,
  17706         .type_name_ctx = block.type_name_ctx,
  17707     };
  17708     defer child_block.instructions.deinit(sema.gpa);
  17709 
  17710     const operand = try sema.resolveInlineBody(&child_block, body, inst);
  17711     return Air.internedToRef(sema.typeOf(operand).toIntern());
  17712 }
  17713 
  17714 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17715     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  17716     const src = block.nodeOffset(inst_data.src_node);
  17717     const operand = try sema.resolveInst(inst_data.operand);
  17718     const operand_ty = sema.typeOf(operand);
  17719     const res_ty = try sema.log2IntType(block, operand_ty, src);
  17720     return Air.internedToRef(res_ty.toIntern());
  17721 }
  17722 
  17723 fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type {
  17724     const pt = sema.pt;
  17725     const zcu = pt.zcu;
  17726     switch (operand.zigTypeTag(zcu)) {
  17727         .comptime_int => return .comptime_int,
  17728         .int => {
  17729             const bits = operand.bitSize(zcu);
  17730             const count = if (bits == 0)
  17731                 0
  17732             else blk: {
  17733                 var count: u16 = 0;
  17734                 var s = bits - 1;
  17735                 while (s != 0) : (s >>= 1) {
  17736                     count += 1;
  17737                 }
  17738                 break :blk count;
  17739             };
  17740             return pt.intType(.unsigned, count);
  17741         },
  17742         .vector => {
  17743             const elem_ty = operand.elemType2(zcu);
  17744             const log2_elem_ty = try sema.log2IntType(block, elem_ty, src);
  17745             return pt.vectorType(.{
  17746                 .len = operand.vectorLen(zcu),
  17747                 .child = log2_elem_ty.toIntern(),
  17748             });
  17749         },
  17750         else => {},
  17751     }
  17752     return sema.fail(
  17753         block,
  17754         src,
  17755         "bit shifting operation expected integer type, found '{f}'",
  17756         .{operand.fmt(pt)},
  17757     );
  17758 }
  17759 
  17760 fn zirTypeofPeer(
  17761     sema: *Sema,
  17762     block: *Block,
  17763     extended: Zir.Inst.Extended.InstData,
  17764     inst: Zir.Inst.Index,
  17765 ) CompileError!Air.Inst.Ref {
  17766     const tracy = trace(@src());
  17767     defer tracy.end();
  17768 
  17769     const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
  17770     const src = block.nodeOffset(extra.data.src_node);
  17771     const body = sema.code.bodySlice(extra.data.body_index, extra.data.body_len);
  17772 
  17773     var child_block: Block = .{
  17774         .parent = block,
  17775         .sema = sema,
  17776         .namespace = block.namespace,
  17777         .instructions = .{},
  17778         .inlining = block.inlining,
  17779         .comptime_reason = null,
  17780         .is_typeof = true,
  17781         .runtime_cond = block.runtime_cond,
  17782         .runtime_loop = block.runtime_loop,
  17783         .runtime_index = block.runtime_index,
  17784         .src_base_inst = block.src_base_inst,
  17785         .type_name_ctx = block.type_name_ctx,
  17786     };
  17787     defer child_block.instructions.deinit(sema.gpa);
  17788     // Ignore the result, we only care about the instructions in `args`.
  17789     _ = try sema.analyzeInlineBody(&child_block, body, inst);
  17790 
  17791     const args = sema.code.refSlice(extra.end, extended.small);
  17792 
  17793     const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len);
  17794     defer sema.gpa.free(inst_list);
  17795 
  17796     for (args, 0..) |arg_ref, i| {
  17797         inst_list[i] = try sema.resolveInst(arg_ref);
  17798     }
  17799 
  17800     const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node });
  17801     return Air.internedToRef(result_type.toIntern());
  17802 }
  17803 
  17804 fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  17805     const pt = sema.pt;
  17806     const zcu = pt.zcu;
  17807     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  17808     const src = block.nodeOffset(inst_data.src_node);
  17809     const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
  17810     const uncasted_operand = try sema.resolveInst(inst_data.operand);
  17811     const uncasted_ty = sema.typeOf(uncasted_operand);
  17812     if (uncasted_ty.isVector(zcu)) {
  17813         if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) {
  17814             return sema.fail(block, operand_src, "boolean not operation on type '{f}'", .{
  17815                 uncasted_ty.fmt(pt),
  17816             });
  17817         }
  17818         return analyzeBitNot(sema, block, uncasted_operand, src);
  17819     }
  17820     const operand = try sema.coerce(block, .bool, uncasted_operand, operand_src);
  17821     if (try sema.resolveValue(operand)) |val| {
  17822         return if (val.isUndef(zcu)) .undef_bool else if (val.toBool()) .bool_false else .bool_true;
  17823     }
  17824     try sema.requireRuntimeBlock(block, src, null);
  17825     return block.addTyOp(.not, .bool, operand);
  17826 }
  17827 
  17828 fn zirBoolBr(
  17829     sema: *Sema,
  17830     parent_block: *Block,
  17831     inst: Zir.Inst.Index,
  17832     is_bool_or: bool,
  17833 ) CompileError!Air.Inst.Ref {
  17834     const tracy = trace(@src());
  17835     defer tracy.end();
  17836 
  17837     const pt = sema.pt;
  17838     const zcu = pt.zcu;
  17839     const gpa = sema.gpa;
  17840 
  17841     const datas = sema.code.instructions.items(.data);
  17842     const inst_data = datas[@intFromEnum(inst)].pl_node;
  17843     const extra = sema.code.extraData(Zir.Inst.BoolBr, inst_data.payload_index);
  17844 
  17845     const uncoerced_lhs = try sema.resolveInst(extra.data.lhs);
  17846     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  17847     const lhs_src = parent_block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
  17848     const rhs_src = parent_block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
  17849 
  17850     const lhs = try sema.coerce(parent_block, .bool, uncoerced_lhs, lhs_src);
  17851 
  17852     if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
  17853         if (is_bool_or and lhs_val.toBool()) {
  17854             return .bool_true;
  17855         } else if (!is_bool_or and !lhs_val.toBool()) {
  17856             return .bool_false;
  17857         }
  17858         // comptime-known left-hand side. No need for a block here; the result
  17859         // is simply the rhs expression. Here we rely on there only being 1
  17860         // break instruction (`break_inline`).
  17861         const rhs_result = try sema.resolveInlineBody(parent_block, body, inst);
  17862         if (sema.typeOf(rhs_result).isNoReturn(zcu)) {
  17863             return rhs_result;
  17864         }
  17865         return sema.coerce(parent_block, .bool, rhs_result, rhs_src);
  17866     }
  17867 
  17868     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  17869     try sema.air_instructions.append(gpa, .{
  17870         .tag = .block,
  17871         .data = .{ .ty_pl = .{
  17872             .ty = .bool_type,
  17873             .payload = undefined,
  17874         } },
  17875     });
  17876 
  17877     var child_block = parent_block.makeSubBlock();
  17878     child_block.runtime_loop = null;
  17879     child_block.runtime_cond = lhs_src;
  17880     child_block.runtime_index.increment();
  17881     defer child_block.instructions.deinit(gpa);
  17882 
  17883     var then_block = child_block.makeSubBlock();
  17884     defer then_block.instructions.deinit(gpa);
  17885 
  17886     var else_block = child_block.makeSubBlock();
  17887     defer else_block.instructions.deinit(gpa);
  17888 
  17889     const lhs_block = if (is_bool_or) &then_block else &else_block;
  17890     const rhs_block = if (is_bool_or) &else_block else &then_block;
  17891 
  17892     const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
  17893     _ = try lhs_block.addBr(block_inst, lhs_result);
  17894 
  17895     const parent_hint = sema.branch_hint;
  17896     defer sema.branch_hint = parent_hint;
  17897     sema.branch_hint = null;
  17898 
  17899     const rhs_result = try sema.resolveInlineBody(rhs_block, body, inst);
  17900     const rhs_noret = sema.typeOf(rhs_result).isNoReturn(zcu);
  17901     const coerced_rhs_result = if (!rhs_noret) rhs: {
  17902         const coerced_result = try sema.coerce(rhs_block, .bool, rhs_result, rhs_src);
  17903         _ = try rhs_block.addBr(block_inst, coerced_result);
  17904         break :rhs coerced_result;
  17905     } else rhs_result;
  17906 
  17907     const rhs_hint = sema.branch_hint orelse .none;
  17908 
  17909     const result = try sema.finishCondBr(
  17910         parent_block,
  17911         &child_block,
  17912         &then_block,
  17913         &else_block,
  17914         lhs,
  17915         block_inst,
  17916         if (is_bool_or) .{
  17917             .true = .none,
  17918             .false = rhs_hint,
  17919             .then_cov = .poi,
  17920             .else_cov = .poi,
  17921         } else .{
  17922             .true = rhs_hint,
  17923             .false = .none,
  17924             .then_cov = .poi,
  17925             .else_cov = .poi,
  17926         },
  17927     );
  17928     if (!rhs_noret) {
  17929         if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| {
  17930             if (is_bool_or and rhs_val.toBool()) {
  17931                 return .bool_true;
  17932             } else if (!is_bool_or and !rhs_val.toBool()) {
  17933                 return .bool_false;
  17934             }
  17935         }
  17936     }
  17937 
  17938     return result;
  17939 }
  17940 
  17941 fn finishCondBr(
  17942     sema: *Sema,
  17943     parent_block: *Block,
  17944     child_block: *Block,
  17945     then_block: *Block,
  17946     else_block: *Block,
  17947     cond: Air.Inst.Ref,
  17948     block_inst: Air.Inst.Index,
  17949     branch_hints: Air.CondBr.BranchHints,
  17950 ) !Air.Inst.Ref {
  17951     const gpa = sema.gpa;
  17952 
  17953     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
  17954         then_block.instructions.items.len + else_block.instructions.items.len +
  17955         @typeInfo(Air.Block).@"struct".fields.len + child_block.instructions.items.len + 1);
  17956 
  17957     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  17958         .then_body_len = @intCast(then_block.instructions.items.len),
  17959         .else_body_len = @intCast(else_block.instructions.items.len),
  17960         .branch_hints = branch_hints,
  17961     });
  17962     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
  17963     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
  17964 
  17965     _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  17966         .operand = cond,
  17967         .payload = cond_br_payload,
  17968     } } });
  17969 
  17970     sema.air_instructions.items(.data)[@intFromEnum(block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(
  17971         Air.Block{ .body_len = @intCast(child_block.instructions.items.len) },
  17972     );
  17973     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
  17974 
  17975     try parent_block.instructions.append(gpa, block_inst);
  17976     return block_inst.toRef();
  17977 }
  17978 
  17979 fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  17980     const pt = sema.pt;
  17981     const zcu = pt.zcu;
  17982     switch (ty.zigTypeTag(zcu)) {
  17983         .optional, .null, .undefined => return,
  17984         .pointer => if (ty.isPtrLikeOptional(zcu)) return,
  17985         else => {},
  17986     }
  17987     return sema.failWithExpectedOptionalType(block, src, ty);
  17988 }
  17989 
  17990 fn checkSentinelType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  17991     const pt = sema.pt;
  17992     const zcu = pt.zcu;
  17993     if (!ty.isSelfComparable(zcu, true)) {
  17994         return sema.fail(block, src, "non-scalar sentinel type '{f}'", .{ty.fmt(pt)});
  17995     }
  17996 }
  17997 
  17998 fn zirIsNonNull(
  17999     sema: *Sema,
  18000     block: *Block,
  18001     inst: Zir.Inst.Index,
  18002 ) CompileError!Air.Inst.Ref {
  18003     const tracy = trace(@src());
  18004     defer tracy.end();
  18005 
  18006     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18007     const src = block.nodeOffset(inst_data.src_node);
  18008     const operand = try sema.resolveInst(inst_data.operand);
  18009     try sema.checkNullableType(block, src, sema.typeOf(operand));
  18010     return sema.analyzeIsNull(block, operand, true);
  18011 }
  18012 
  18013 fn zirIsNonNullPtr(
  18014     sema: *Sema,
  18015     block: *Block,
  18016     inst: Zir.Inst.Index,
  18017 ) CompileError!Air.Inst.Ref {
  18018     const tracy = trace(@src());
  18019     defer tracy.end();
  18020 
  18021     const pt = sema.pt;
  18022     const zcu = pt.zcu;
  18023     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18024     const src = block.nodeOffset(inst_data.src_node);
  18025     const ptr = try sema.resolveInst(inst_data.operand);
  18026     const ptr_ty = sema.typeOf(ptr);
  18027     try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(zcu));
  18028     if (try sema.resolveValue(ptr)) |ptr_val| {
  18029         if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |loaded_val| {
  18030             return sema.analyzeIsNull(block, Air.internedToRef(loaded_val.toIntern()), true);
  18031         }
  18032     }
  18033     if (ptr_ty.childType(zcu).isNullFromType(zcu)) |is_null| {
  18034         return if (is_null) .bool_false else .bool_true;
  18035     }
  18036     return block.addUnOp(.is_non_null_ptr, ptr);
  18037 }
  18038 
  18039 fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  18040     const pt = sema.pt;
  18041     const zcu = pt.zcu;
  18042     switch (ty.zigTypeTag(zcu)) {
  18043         .error_set, .error_union, .undefined => return,
  18044         else => return sema.fail(block, src, "expected error union type, found '{f}'", .{
  18045             ty.fmt(pt),
  18046         }),
  18047     }
  18048 }
  18049 
  18050 fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18051     const tracy = trace(@src());
  18052     defer tracy.end();
  18053 
  18054     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18055     const src = block.nodeOffset(inst_data.src_node);
  18056     const operand = try sema.resolveInst(inst_data.operand);
  18057     try sema.checkErrorType(block, src, sema.typeOf(operand));
  18058     return sema.analyzeIsNonErr(block, src, operand);
  18059 }
  18060 
  18061 fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18062     const tracy = trace(@src());
  18063     defer tracy.end();
  18064 
  18065     const pt = sema.pt;
  18066     const zcu = pt.zcu;
  18067     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18068     const src = block.nodeOffset(inst_data.src_node);
  18069     const ptr = try sema.resolveInst(inst_data.operand);
  18070     try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(zcu));
  18071     const loaded = try sema.analyzeLoad(block, src, ptr, src);
  18072     return sema.analyzeIsNonErr(block, src, loaded);
  18073 }
  18074 
  18075 fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18076     const tracy = trace(@src());
  18077     defer tracy.end();
  18078 
  18079     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18080     const src = block.nodeOffset(inst_data.src_node);
  18081     const operand = try sema.resolveInst(inst_data.operand);
  18082     return sema.analyzeIsNonErr(block, src, operand);
  18083 }
  18084 
  18085 fn zirCondbr(
  18086     sema: *Sema,
  18087     parent_block: *Block,
  18088     inst: Zir.Inst.Index,
  18089 ) CompileError!void {
  18090     const tracy = trace(@src());
  18091     defer tracy.end();
  18092 
  18093     const pt = sema.pt;
  18094     const zcu = pt.zcu;
  18095     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18096     const cond_src = parent_block.src(.{ .node_offset_if_cond = inst_data.src_node });
  18097     const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
  18098 
  18099     const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
  18100     const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
  18101 
  18102     const uncasted_cond = try sema.resolveInst(extra.data.condition);
  18103     const cond = try sema.coerce(parent_block, .bool, uncasted_cond, cond_src);
  18104 
  18105     if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| {
  18106         const body = if (cond_val.toBool()) then_body else else_body;
  18107 
  18108         // We can propagate `.cold` hints from this branch since it's comptime-known
  18109         // to be taken from the parent branch.
  18110         const parent_hint = sema.branch_hint;
  18111         defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
  18112 
  18113         try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src);
  18114         // We use `analyzeBodyInner` since we want to propagate any comptime control flow to the caller.
  18115         return sema.analyzeBodyInner(parent_block, body);
  18116     }
  18117 
  18118     const gpa = sema.gpa;
  18119 
  18120     // We'll re-use the sub block to save on memory bandwidth, and yank out the
  18121     // instructions array in between using it for the then block and else block.
  18122     var sub_block = parent_block.makeSubBlock();
  18123     sub_block.runtime_loop = null;
  18124     sub_block.runtime_cond = cond_src;
  18125     sub_block.runtime_index.increment();
  18126     sub_block.need_debug_scope = null; // this body is emitted regardless
  18127     defer sub_block.instructions.deinit(gpa);
  18128 
  18129     const true_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
  18130     const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
  18131     defer gpa.free(true_instructions);
  18132 
  18133     const err_cond = blk: {
  18134         const index = extra.data.condition.toIndex() orelse break :blk null;
  18135         if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) break :blk null;
  18136 
  18137         const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node;
  18138         const err_operand = try sema.resolveInst(err_inst_data.operand);
  18139         const operand_ty = sema.typeOf(err_operand);
  18140         assert(operand_ty.zigTypeTag(zcu) == .error_union);
  18141         const result_ty = operand_ty.errorUnionSet(zcu);
  18142         break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);
  18143     };
  18144 
  18145     const false_hint: std.builtin.BranchHint = if (err_cond != null and
  18146         try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src, false))
  18147     h: {
  18148         // nothing to do here. weight against error branch
  18149         break :h .unlikely;
  18150     } else try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
  18151 
  18152     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
  18153         true_instructions.len + sub_block.instructions.items.len);
  18154     _ = try parent_block.addInst(.{
  18155         .tag = .cond_br,
  18156         .data = .{
  18157             .pl_op = .{
  18158                 .operand = cond,
  18159                 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18160                     .then_body_len = @intCast(true_instructions.len),
  18161                     .else_body_len = @intCast(sub_block.instructions.items.len),
  18162                     .branch_hints = .{
  18163                         .true = true_hint,
  18164                         .false = false_hint,
  18165                         // Code coverage is desired for error handling.
  18166                         .then_cov = .poi,
  18167                         .else_cov = .poi,
  18168                     },
  18169                 }),
  18170             },
  18171         },
  18172     });
  18173     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
  18174     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  18175 }
  18176 
  18177 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18178     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18179     const src = parent_block.nodeOffset(inst_data.src_node);
  18180     const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node });
  18181     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18182     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  18183     const err_union = try sema.resolveInst(extra.data.operand);
  18184     const err_union_ty = sema.typeOf(err_union);
  18185     const pt = sema.pt;
  18186     const zcu = pt.zcu;
  18187     if (err_union_ty.zigTypeTag(zcu) != .error_union) {
  18188         return sema.failWithOwnedErrorMsg(parent_block, msg: {
  18189             const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
  18190             errdefer msg.destroy(sema.gpa);
  18191             try sema.addDeclaredHereNote(msg, err_union_ty);
  18192             try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
  18193             break :msg msg;
  18194         });
  18195     }
  18196     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18197     if (is_non_err != .none) {
  18198         // We can propagate `.cold` hints from this branch since it's comptime-known
  18199         // to be taken from the parent branch.
  18200         const parent_hint = sema.branch_hint;
  18201         defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
  18202 
  18203         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18204         if (is_non_err_val.toBool()) {
  18205             return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
  18206         }
  18207         // We can analyze the body directly in the parent block because we know there are
  18208         // no breaks from the body possible, and that the body is noreturn.
  18209         try sema.analyzeBodyInner(parent_block, body);
  18210         return .unreachable_value;
  18211     }
  18212 
  18213     var sub_block = parent_block.makeSubBlock();
  18214     defer sub_block.instructions.deinit(sema.gpa);
  18215 
  18216     const parent_hint = sema.branch_hint;
  18217     defer sema.branch_hint = parent_hint;
  18218 
  18219     // This body is guaranteed to end with noreturn and has no breaks.
  18220     try sema.analyzeBodyInner(&sub_block, body);
  18221 
  18222     // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`.
  18223     const is_cold = sema.branch_hint == .cold;
  18224 
  18225     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).@"struct".fields.len +
  18226         sub_block.instructions.items.len);
  18227     const try_inst = try parent_block.addInst(.{
  18228         .tag = if (is_cold) .try_cold else .@"try",
  18229         .data = .{ .pl_op = .{
  18230             .operand = err_union,
  18231             .payload = sema.addExtraAssumeCapacity(Air.Try{
  18232                 .body_len = @intCast(sub_block.instructions.items.len),
  18233             }),
  18234         } },
  18235     });
  18236     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  18237     return try_inst;
  18238 }
  18239 
  18240 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18241     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  18242     const src = parent_block.nodeOffset(inst_data.src_node);
  18243     const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node });
  18244     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
  18245     const body = sema.code.bodySlice(extra.end, extra.data.body_len);
  18246     const operand = try sema.resolveInst(extra.data.operand);
  18247     const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
  18248     const err_union_ty = sema.typeOf(err_union);
  18249     const pt = sema.pt;
  18250     const zcu = pt.zcu;
  18251     if (err_union_ty.zigTypeTag(zcu) != .error_union) {
  18252         return sema.failWithOwnedErrorMsg(parent_block, msg: {
  18253             const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
  18254             errdefer msg.destroy(sema.gpa);
  18255             try sema.addDeclaredHereNote(msg, err_union_ty);
  18256             try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
  18257             break :msg msg;
  18258         });
  18259     }
  18260     const is_non_err = try sema.analyzeIsNonErrComptimeOnly(parent_block, operand_src, err_union);
  18261     if (is_non_err != .none) {
  18262         // We can propagate `.cold` hints from this branch since it's comptime-known
  18263         // to be taken from the parent branch.
  18264         const parent_hint = sema.branch_hint;
  18265         defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
  18266 
  18267         const is_non_err_val = (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)).?;
  18268         if (is_non_err_val.toBool()) {
  18269             return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
  18270         }
  18271         // We can analyze the body directly in the parent block because we know there are
  18272         // no breaks from the body possible, and that the body is noreturn.
  18273         try sema.analyzeBodyInner(parent_block, body);
  18274         return .unreachable_value;
  18275     }
  18276 
  18277     var sub_block = parent_block.makeSubBlock();
  18278     defer sub_block.instructions.deinit(sema.gpa);
  18279 
  18280     const parent_hint = sema.branch_hint;
  18281     defer sema.branch_hint = parent_hint;
  18282 
  18283     // This body is guaranteed to end with noreturn and has no breaks.
  18284     try sema.analyzeBodyInner(&sub_block, body);
  18285 
  18286     // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`.
  18287     const is_cold = sema.branch_hint == .cold;
  18288 
  18289     const operand_ty = sema.typeOf(operand);
  18290     const ptr_info = operand_ty.ptrInfo(zcu);
  18291     const res_ty = try pt.ptrTypeSema(.{
  18292         .child = err_union_ty.errorUnionPayload(zcu).toIntern(),
  18293         .flags = .{
  18294             .is_const = ptr_info.flags.is_const,
  18295             .is_volatile = ptr_info.flags.is_volatile,
  18296             .is_allowzero = ptr_info.flags.is_allowzero,
  18297             .address_space = ptr_info.flags.address_space,
  18298         },
  18299     });
  18300     const res_ty_ref = Air.internedToRef(res_ty.toIntern());
  18301     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).@"struct".fields.len +
  18302         sub_block.instructions.items.len);
  18303     const try_inst = try parent_block.addInst(.{
  18304         .tag = if (is_cold) .try_ptr_cold else .try_ptr,
  18305         .data = .{ .ty_pl = .{
  18306             .ty = res_ty_ref,
  18307             .payload = sema.addExtraAssumeCapacity(Air.TryPtr{
  18308                 .ptr = operand,
  18309                 .body_len = @intCast(sub_block.instructions.items.len),
  18310             }),
  18311         } },
  18312     });
  18313     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
  18314     return try_inst;
  18315 }
  18316 
  18317 fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*LabeledBlock {
  18318     const gop = sema.inst_map.getOrPutAssumeCapacity(dest_block);
  18319     if (gop.found_existing) existing: {
  18320         // This may be a *result* from an earlier iteration of an inline loop.
  18321         // In this case, there will not be a post-hoc block entry, and we can
  18322         // continue with the logic below.
  18323         const new_block_inst = gop.value_ptr.*.toIndex() orelse break :existing;
  18324         return sema.post_hoc_blocks.get(new_block_inst) orelse break :existing;
  18325     }
  18326 
  18327     try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1);
  18328 
  18329     const new_block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  18330     gop.value_ptr.* = new_block_inst.toRef();
  18331     try sema.air_instructions.append(sema.gpa, .{
  18332         .tag = .block,
  18333         .data = undefined,
  18334     });
  18335     const labeled_block = try sema.gpa.create(LabeledBlock);
  18336     labeled_block.* = .{
  18337         .label = .{
  18338             .zir_block = dest_block,
  18339             .merges = .{
  18340                 .src_locs = .{},
  18341                 .results = .{},
  18342                 .br_list = .{},
  18343                 .block_inst = new_block_inst,
  18344             },
  18345         },
  18346         .block = .{
  18347             .parent = block,
  18348             .sema = sema,
  18349             .namespace = block.namespace,
  18350             .instructions = .{},
  18351             .label = &labeled_block.label,
  18352             .inlining = block.inlining,
  18353             .comptime_reason = block.comptime_reason,
  18354             .src_base_inst = block.src_base_inst,
  18355             .type_name_ctx = block.type_name_ctx,
  18356         },
  18357     };
  18358     sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block);
  18359     return labeled_block;
  18360 }
  18361 
  18362 /// A `break` statement is inside a runtime condition, but trying to
  18363 /// break from an inline loop. In such case we must convert it to
  18364 /// a runtime break.
  18365 fn addRuntimeBreak(sema: *Sema, child_block: *Block, block_inst: Zir.Inst.Index, break_operand: Zir.Inst.Ref) !void {
  18366     const labeled_block = try sema.ensurePostHoc(child_block, block_inst);
  18367 
  18368     const operand = try sema.resolveInst(break_operand);
  18369     const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand);
  18370 
  18371     try labeled_block.label.merges.results.append(sema.gpa, operand);
  18372     try labeled_block.label.merges.br_list.append(sema.gpa, br_ref.toIndex().?);
  18373     try labeled_block.label.merges.src_locs.append(sema.gpa, null);
  18374 
  18375     labeled_block.block.runtime_index.increment();
  18376     if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) {
  18377         labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop;
  18378         labeled_block.block.runtime_loop = child_block.runtime_loop;
  18379     }
  18380 }
  18381 
  18382 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18383     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
  18384     const src = block.nodeOffset(inst_data.src_node);
  18385 
  18386     if (block.isComptime()) {
  18387         return sema.fail(block, src, "reached unreachable code", .{});
  18388     }
  18389     // TODO Add compile error for @optimizeFor occurring too late in a scope.
  18390     sema.analyzeUnreachable(block, src, true) catch |err| switch (err) {
  18391         error.AnalysisFail => {
  18392             const msg = sema.err orelse return err;
  18393             if (!mem.eql(u8, msg.msg, "runtime safety check not allowed in naked function")) return err;
  18394             try sema.errNote(src, msg, "the end of a naked function is implicitly unreachable", .{});
  18395             return err;
  18396         },
  18397         else => |e| return e,
  18398     };
  18399 }
  18400 
  18401 fn zirRetErrValue(
  18402     sema: *Sema,
  18403     block: *Block,
  18404     inst: Zir.Inst.Index,
  18405 ) CompileError!void {
  18406     const pt = sema.pt;
  18407     const zcu = pt.zcu;
  18408     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
  18409     const src = block.tokenOffset(inst_data.src_tok);
  18410     const err_name = try zcu.intern_pool.getOrPutString(
  18411         sema.gpa,
  18412         pt.tid,
  18413         inst_data.get(sema.code),
  18414         .no_embedded_nulls,
  18415     );
  18416     _ = try pt.getErrorValue(err_name);
  18417     // Return the error code from the function.
  18418     const error_set_type = try pt.singleErrorSetType(err_name);
  18419     const result_inst = Air.internedToRef((try pt.intern(.{ .err = .{
  18420         .ty = error_set_type.toIntern(),
  18421         .name = err_name,
  18422     } })));
  18423     return sema.analyzeRet(block, result_inst, src, src);
  18424 }
  18425 
  18426 fn zirRetImplicit(
  18427     sema: *Sema,
  18428     block: *Block,
  18429     inst: Zir.Inst.Index,
  18430 ) CompileError!void {
  18431     const tracy = trace(@src());
  18432     defer tracy.end();
  18433 
  18434     const pt = sema.pt;
  18435     const zcu = pt.zcu;
  18436     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
  18437     const r_brace_src = block.tokenOffset(inst_data.src_tok);
  18438     if (block.inlining == null and sema.func_is_naked) {
  18439         assert(!block.isComptime());
  18440         if (block.wantSafety()) {
  18441             // Calling a safety function from a naked function would not be legal.
  18442             _ = try block.addNoOp(.trap);
  18443         } else {
  18444             try sema.analyzeUnreachable(block, r_brace_src, false);
  18445         }
  18446         return;
  18447     }
  18448 
  18449     const operand = try sema.resolveInst(inst_data.operand);
  18450     const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = .zero });
  18451     const base_tag = sema.fn_ret_ty.baseZigTypeTag(zcu);
  18452     if (base_tag == .noreturn) {
  18453         const msg = msg: {
  18454             const msg = try sema.errMsg(ret_ty_src, "function declared '{f}' implicitly returns", .{
  18455                 sema.fn_ret_ty.fmt(pt),
  18456             });
  18457             errdefer msg.destroy(sema.gpa);
  18458             try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{});
  18459             break :msg msg;
  18460         };
  18461         return sema.failWithOwnedErrorMsg(block, msg);
  18462     } else if (base_tag != .void) {
  18463         const msg = msg: {
  18464             const msg = try sema.errMsg(ret_ty_src, "function with non-void return type '{f}' implicitly returns", .{
  18465                 sema.fn_ret_ty.fmt(pt),
  18466             });
  18467             errdefer msg.destroy(sema.gpa);
  18468             try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{});
  18469             break :msg msg;
  18470         };
  18471         return sema.failWithOwnedErrorMsg(block, msg);
  18472     }
  18473 
  18474     return sema.analyzeRet(block, operand, r_brace_src, r_brace_src);
  18475 }
  18476 
  18477 fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18478     const tracy = trace(@src());
  18479     defer tracy.end();
  18480 
  18481     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18482     const operand = try sema.resolveInst(inst_data.operand);
  18483     const src = block.nodeOffset(inst_data.src_node);
  18484 
  18485     return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node }));
  18486 }
  18487 
  18488 fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18489     const tracy = trace(@src());
  18490     defer tracy.end();
  18491 
  18492     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18493     const src = block.nodeOffset(inst_data.src_node);
  18494     const ret_ptr = try sema.resolveInst(inst_data.operand);
  18495 
  18496     if (block.isComptime() or block.inlining != null or sema.func_is_naked) {
  18497         const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
  18498         return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node }));
  18499     }
  18500 
  18501     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  18502         const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
  18503         return sema.retWithErrTracing(block, src, is_non_err, .ret_load, ret_ptr);
  18504     }
  18505 
  18506     _ = try block.addUnOp(.ret_load, ret_ptr);
  18507 }
  18508 
  18509 fn retWithErrTracing(
  18510     sema: *Sema,
  18511     block: *Block,
  18512     src: LazySrcLoc,
  18513     is_non_err: Air.Inst.Ref,
  18514     ret_tag: Air.Inst.Tag,
  18515     operand: Air.Inst.Ref,
  18516 ) CompileError!void {
  18517     const pt = sema.pt;
  18518     const need_check = switch (is_non_err) {
  18519         .bool_true => {
  18520             _ = try block.addUnOp(ret_tag, operand);
  18521             return;
  18522         },
  18523         .bool_false => false,
  18524         else => true,
  18525     };
  18526 
  18527     // This means we're returning something that might be an error!
  18528     // This should only be possible with the `auto` cc, so we definitely have an error trace.
  18529     assert(pt.zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace);
  18530 
  18531     const gpa = sema.gpa;
  18532     const return_err_fn = Air.internedToRef(try sema.getBuiltin(src, .returnError));
  18533 
  18534     if (!need_check) {
  18535         try sema.callBuiltin(block, src, return_err_fn, .never_tail, &.{}, .@"error return");
  18536         _ = try block.addUnOp(ret_tag, operand);
  18537         return;
  18538     }
  18539 
  18540     var then_block = block.makeSubBlock();
  18541     defer then_block.instructions.deinit(gpa);
  18542     _ = try then_block.addUnOp(ret_tag, operand);
  18543 
  18544     var else_block = block.makeSubBlock();
  18545     defer else_block.instructions.deinit(gpa);
  18546     try sema.callBuiltin(&else_block, src, return_err_fn, .never_tail, &.{}, .@"error return");
  18547     _ = try else_block.addUnOp(ret_tag, operand);
  18548 
  18549     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".fields.len +
  18550         then_block.instructions.items.len + else_block.instructions.items.len +
  18551         @typeInfo(Air.Block).@"struct".fields.len + 1);
  18552 
  18553     const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
  18554         .then_body_len = @intCast(then_block.instructions.items.len),
  18555         .else_body_len = @intCast(else_block.instructions.items.len),
  18556         .branch_hints = .{
  18557             // Weight against error branch.
  18558             .true = .likely,
  18559             .false = .unlikely,
  18560             // Code coverage is not valuable on either branch.
  18561             .then_cov = .none,
  18562             .else_cov = .none,
  18563         },
  18564     });
  18565     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
  18566     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
  18567 
  18568     _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
  18569         .operand = is_non_err,
  18570         .payload = cond_br_payload,
  18571     } } });
  18572 }
  18573 
  18574 fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
  18575     const pt = sema.pt;
  18576     const zcu = pt.zcu;
  18577     return fn_ret_ty.isError(zcu) and zcu.comp.config.any_error_tracing;
  18578 }
  18579 
  18580 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  18581     const pt = sema.pt;
  18582     const zcu = pt.zcu;
  18583     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index;
  18584 
  18585     if (!block.ownerModule().error_tracing) return;
  18586 
  18587     // This is only relevant at runtime.
  18588     if (block.isComptime() or block.is_typeof) return;
  18589 
  18590     const save_index = inst_data.operand == .none or b: {
  18591         const operand = try sema.resolveInst(inst_data.operand);
  18592         const operand_ty = sema.typeOf(operand);
  18593         break :b operand_ty.isError(zcu);
  18594     };
  18595 
  18596     if (save_index)
  18597         block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block);
  18598 }
  18599 
  18600 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
  18601     const extra = sema.code.extraData(Zir.Inst.RestoreErrRetIndex, extended.operand).data;
  18602     return sema.restoreErrRetIndex(start_block, start_block.nodeOffset(extra.src_node), extra.block, extra.operand);
  18603 }
  18604 
  18605 /// If `operand` is non-error (or is `none`), restores the error return trace to
  18606 /// its state at the point `block` was reached (or, if `block` is `none`, the
  18607 /// point this function began execution).
  18608 fn restoreErrRetIndex(sema: *Sema, start_block: *Block, src: LazySrcLoc, target_block: Zir.Inst.Ref, operand_zir: Zir.Inst.Ref) CompileError!void {
  18609     const tracy = trace(@src());
  18610     defer tracy.end();
  18611 
  18612     const pt = sema.pt;
  18613     const zcu = pt.zcu;
  18614 
  18615     const saved_index = if (target_block.toIndexAllowNone()) |zir_block| b: {
  18616         var block = start_block;
  18617         while (true) {
  18618             if (block.label) |label| {
  18619                 if (label.zir_block == zir_block) {
  18620                     const target_trace_index = if (block.parent) |parent_block| tgt: {
  18621                         break :tgt parent_block.error_return_trace_index;
  18622                     } else sema.error_return_trace_index_on_fn_entry;
  18623 
  18624                     if (start_block.error_return_trace_index != target_trace_index)
  18625                         break :b target_trace_index;
  18626 
  18627                     return; // No need to restore
  18628                 }
  18629             }
  18630             block = block.parent.?;
  18631         }
  18632     } else b: {
  18633         if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry)
  18634             break :b sema.error_return_trace_index_on_fn_entry;
  18635 
  18636         return; // No need to restore
  18637     };
  18638 
  18639     const operand = try sema.resolveInstAllowNone(operand_zir);
  18640 
  18641     if (start_block.isComptime() or start_block.is_typeof) {
  18642         const is_non_error = if (operand != .none) blk: {
  18643             const is_non_error_inst = try sema.analyzeIsNonErr(start_block, src, operand);
  18644             const cond_val = try sema.resolveDefinedValue(start_block, src, is_non_error_inst);
  18645             break :blk cond_val.?.toBool();
  18646         } else true; // no operand means pop unconditionally
  18647 
  18648         if (is_non_error) return;
  18649 
  18650         const saved_index_val = try sema.resolveDefinedValue(start_block, src, saved_index);
  18651         const saved_index_int = saved_index_val.?.toUnsignedInt(zcu);
  18652         assert(saved_index_int <= sema.comptime_err_ret_trace.items.len);
  18653         sema.comptime_err_ret_trace.items.len = @intCast(saved_index_int);
  18654         return;
  18655     }
  18656 
  18657     if (!zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace) return;
  18658     if (!start_block.ownerModule().error_tracing) return;
  18659 
  18660     assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere
  18661 
  18662     return sema.popErrorReturnTrace(start_block, src, operand, saved_index);
  18663 }
  18664 
  18665 fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
  18666     const pt = sema.pt;
  18667     const zcu = pt.zcu;
  18668     const ip = &zcu.intern_pool;
  18669     assert(sema.fn_ret_ty.zigTypeTag(zcu) == .error_union);
  18670     const err_set_ty = sema.fn_ret_ty.errorUnionSet(zcu).toIntern();
  18671     switch (err_set_ty) {
  18672         .adhoc_inferred_error_set_type => {
  18673             const ies = sema.fn_ret_ty_ies.?;
  18674             assert(ies.func == .none);
  18675             try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand));
  18676         },
  18677         else => if (ip.isInferredErrorSetType(err_set_ty)) {
  18678             const ies = sema.fn_ret_ty_ies.?;
  18679             assert(ies.func == sema.owner.unwrap().func);
  18680             try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand));
  18681         },
  18682     }
  18683 }
  18684 
  18685 fn addToInferredErrorSetPtr(sema: *Sema, ies: *InferredErrorSet, op_ty: Type) !void {
  18686     const arena = sema.arena;
  18687     const pt = sema.pt;
  18688     const zcu = pt.zcu;
  18689     const ip = &zcu.intern_pool;
  18690     switch (op_ty.zigTypeTag(zcu)) {
  18691         .error_set => try ies.addErrorSet(op_ty, ip, arena),
  18692         .error_union => try ies.addErrorSet(op_ty.errorUnionSet(zcu), ip, arena),
  18693         else => {},
  18694     }
  18695 }
  18696 
  18697 fn analyzeRet(
  18698     sema: *Sema,
  18699     block: *Block,
  18700     uncasted_operand: Air.Inst.Ref,
  18701     src: LazySrcLoc,
  18702     operand_src: LazySrcLoc,
  18703 ) CompileError!void {
  18704     // Special case for returning an error to an inferred error set; we need to
  18705     // add the error tag to the inferred error set of the in-scope function, so
  18706     // that the coercion below works correctly.
  18707     const pt = sema.pt;
  18708     const zcu = pt.zcu;
  18709     if (sema.fn_ret_ty_ies != null and sema.fn_ret_ty.zigTypeTag(zcu) == .error_union) {
  18710         try sema.addToInferredErrorSet(uncasted_operand);
  18711     }
  18712     const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, operand_src, .{ .is_ret = true }) catch |err| switch (err) {
  18713         error.NotCoercible => unreachable,
  18714         else => |e| return e,
  18715     };
  18716 
  18717     if (block.inlining) |inlining| {
  18718         assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr
  18719         if (block.isComptime()) {
  18720             const ret_val = try sema.resolveConstValue(block, operand_src, operand, null);
  18721             inlining.comptime_result = operand;
  18722 
  18723             if (sema.fn_ret_ty.isError(zcu) and ret_val.getErrorName(zcu) != .none) {
  18724                 try sema.comptime_err_ret_trace.append(src);
  18725             }
  18726             return error.ComptimeReturn;
  18727         }
  18728         // We are inlining a function call; rewrite the `ret` as a `break`.
  18729         const br_inst = try block.addBr(inlining.merges.block_inst, operand);
  18730         try inlining.merges.results.append(sema.gpa, operand);
  18731         try inlining.merges.br_list.append(sema.gpa, br_inst.toIndex().?);
  18732         try inlining.merges.src_locs.append(sema.gpa, operand_src);
  18733         return;
  18734     } else if (block.isComptime()) {
  18735         return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
  18736     } else if (sema.func_is_naked) {
  18737         const msg = msg: {
  18738             const msg = try sema.errMsg(src, "cannot return from naked function", .{});
  18739             errdefer msg.destroy(sema.gpa);
  18740 
  18741             try sema.errNote(src, msg, "can only return using assembly", .{});
  18742             break :msg msg;
  18743         };
  18744         return sema.failWithOwnedErrorMsg(block, msg);
  18745     }
  18746 
  18747     try sema.fn_ret_ty.resolveLayout(pt);
  18748 
  18749     try sema.validateRuntimeValue(block, operand_src, operand);
  18750 
  18751     const air_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret;
  18752     if (sema.wantErrorReturnTracing(sema.fn_ret_ty)) {
  18753         // Avoid adding a frame to the error return trace in case the value is comptime-known
  18754         // to be not an error.
  18755         const is_non_err = try sema.analyzeIsNonErr(block, operand_src, operand);
  18756         return sema.retWithErrTracing(block, src, is_non_err, air_tag, operand);
  18757     }
  18758 
  18759     _ = try block.addUnOp(air_tag, operand);
  18760 }
  18761 
  18762 fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
  18763     // extend this swich as additional operators are implemented
  18764     return switch (tag) {
  18765         .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .mod, .rem, .mod_rem => true,
  18766         else => false,
  18767     };
  18768 }
  18769 
  18770 fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18771     const tracy = trace(@src());
  18772     defer tracy.end();
  18773 
  18774     const pt = sema.pt;
  18775     const zcu = pt.zcu;
  18776     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].ptr_type;
  18777     const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index);
  18778     const elem_ty_src = block.src(.{ .node_offset_ptr_elem = extra.data.src_node });
  18779     const sentinel_src = block.src(.{ .node_offset_ptr_sentinel = extra.data.src_node });
  18780     const align_src = block.src(.{ .node_offset_ptr_align = extra.data.src_node });
  18781     const addrspace_src = block.src(.{ .node_offset_ptr_addrspace = extra.data.src_node });
  18782     const bitoffset_src = block.src(.{ .node_offset_ptr_bitoffset = extra.data.src_node });
  18783     const hostsize_src = block.src(.{ .node_offset_ptr_hostsize = extra.data.src_node });
  18784 
  18785     const elem_ty = blk: {
  18786         const air_inst = try sema.resolveInst(extra.data.elem_type);
  18787         const ty = sema.analyzeAsType(block, elem_ty_src, air_inst) catch |err| {
  18788             if (err == error.AnalysisFail and sema.err != null and sema.typeOf(air_inst).isSinglePointer(zcu)) {
  18789                 try sema.errNote(elem_ty_src, sema.err.?, "use '.*' to dereference pointer", .{});
  18790             }
  18791             return err;
  18792         };
  18793         assert(!ty.isGenericPoison());
  18794         break :blk ty;
  18795     };
  18796 
  18797     if (elem_ty.zigTypeTag(zcu) == .noreturn)
  18798         return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{});
  18799 
  18800     const target = zcu.getTarget();
  18801 
  18802     var extra_i = extra.end;
  18803 
  18804     const sentinel = if (inst_data.flags.has_sentinel) blk: {
  18805         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  18806         extra_i += 1;
  18807         const coerced = try sema.coerce(block, elem_ty, try sema.resolveInst(ref), sentinel_src);
  18808         const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{ .simple = .pointer_sentinel });
  18809         try checkSentinelType(sema, block, sentinel_src, elem_ty);
  18810         break :blk val.toIntern();
  18811     } else .none;
  18812 
  18813     const abi_align: Alignment = if (inst_data.flags.has_align) blk: {
  18814         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  18815         extra_i += 1;
  18816         const coerced = try sema.coerce(block, align_ty, try sema.resolveInst(ref), align_src);
  18817         const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" });
  18818         // Check if this happens to be the lazy alignment of our element type, in
  18819         // which case we can make this 0 without resolving it.
  18820         switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  18821             .int => |int| switch (int.storage) {
  18822                 .lazy_align => |lazy_ty| if (lazy_ty == elem_ty.toIntern()) break :blk .none,
  18823                 else => {},
  18824             },
  18825             else => {},
  18826         }
  18827         const align_bytes = (try val.getUnsignedIntSema(pt)).?;
  18828         break :blk try sema.validateAlign(block, align_src, align_bytes);
  18829     } else .none;
  18830 
  18831     const address_space: std.builtin.AddressSpace = if (inst_data.flags.has_addrspace) blk: {
  18832         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  18833         extra_i += 1;
  18834         break :blk try sema.resolveAddressSpace(block, addrspace_src, ref, .pointer);
  18835     } else if (elem_ty.zigTypeTag(zcu) == .@"fn" and target.cpu.arch == .avr) .flash else .generic;
  18836 
  18837     const bit_offset: u16 = if (inst_data.flags.has_bit_range) blk: {
  18838         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  18839         extra_i += 1;
  18840         const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, .u16, .{ .simple = .type });
  18841         break :blk @intCast(bit_offset);
  18842     } else 0;
  18843 
  18844     const host_size: u16 = if (inst_data.flags.has_bit_range) blk: {
  18845         const ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_i]);
  18846         extra_i += 1;
  18847         const host_size = try sema.resolveInt(block, hostsize_src, ref, .u16, .{ .simple = .type });
  18848         break :blk @intCast(host_size);
  18849     } else 0;
  18850 
  18851     if (host_size != 0) {
  18852         if (bit_offset >= host_size * 8) {
  18853             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", .{
  18854                 elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size,
  18855             });
  18856         }
  18857         const elem_bit_size = try elem_ty.bitSizeSema(pt);
  18858         if (elem_bit_size > host_size * 8 - bit_offset) {
  18859             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", .{
  18860                 elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size,
  18861             });
  18862         }
  18863     }
  18864 
  18865     if (elem_ty.zigTypeTag(zcu) == .@"fn") {
  18866         if (inst_data.size != .one) {
  18867             return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{});
  18868         }
  18869     } else if (inst_data.size == .many and elem_ty.zigTypeTag(zcu) == .@"opaque") {
  18870         return sema.fail(block, elem_ty_src, "unknown-length pointer to opaque not allowed", .{});
  18871     } else if (inst_data.size == .c) {
  18872         if (!try sema.validateExternType(elem_ty, .other)) {
  18873             const msg = msg: {
  18874                 const msg = try sema.errMsg(elem_ty_src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)});
  18875                 errdefer msg.destroy(sema.gpa);
  18876 
  18877                 try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src, elem_ty, .other);
  18878 
  18879                 try sema.addDeclaredHereNote(msg, elem_ty);
  18880                 break :msg msg;
  18881             };
  18882             return sema.failWithOwnedErrorMsg(block, msg);
  18883         }
  18884         if (elem_ty.zigTypeTag(zcu) == .@"opaque") {
  18885             return sema.fail(block, elem_ty_src, "C pointers cannot point to opaque types", .{});
  18886         }
  18887     }
  18888 
  18889     if (host_size != 0 and !try sema.validatePackedType(elem_ty)) {
  18890         return sema.failWithOwnedErrorMsg(block, msg: {
  18891             const msg = try sema.errMsg(elem_ty_src, "bit-pointer cannot refer to value of type '{f}'", .{elem_ty.fmt(pt)});
  18892             errdefer msg.destroy(sema.gpa);
  18893             try sema.explainWhyTypeIsNotPacked(msg, elem_ty_src, elem_ty);
  18894             break :msg msg;
  18895         });
  18896     }
  18897 
  18898     const ty = try pt.ptrTypeSema(.{
  18899         .child = elem_ty.toIntern(),
  18900         .sentinel = sentinel,
  18901         .flags = .{
  18902             .alignment = abi_align,
  18903             .address_space = address_space,
  18904             .is_const = !inst_data.flags.is_mutable,
  18905             .is_allowzero = inst_data.flags.is_allowzero,
  18906             .is_volatile = inst_data.flags.is_volatile,
  18907             .size = inst_data.size,
  18908         },
  18909         .packed_offset = .{
  18910             .bit_offset = bit_offset,
  18911             .host_size = host_size,
  18912         },
  18913     });
  18914     return Air.internedToRef(ty.toIntern());
  18915 }
  18916 
  18917 fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  18918     const tracy = trace(@src());
  18919     defer tracy.end();
  18920 
  18921     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18922     const src = block.nodeOffset(inst_data.src_node);
  18923     const ty_src = block.src(.{ .node_offset_init_ty = inst_data.src_node });
  18924     const obj_ty = try sema.resolveType(block, ty_src, inst_data.operand);
  18925     const pt = sema.pt;
  18926     const zcu = pt.zcu;
  18927 
  18928     switch (obj_ty.zigTypeTag(zcu)) {
  18929         .@"struct" => return sema.structInitEmpty(block, obj_ty, src, src),
  18930         .array, .vector => return sema.arrayInitEmpty(block, src, obj_ty),
  18931         .void => return Air.internedToRef(Value.void.toIntern()),
  18932         .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}),
  18933         else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
  18934     }
  18935 }
  18936 
  18937 fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_byref: bool) CompileError!Air.Inst.Ref {
  18938     const tracy = trace(@src());
  18939     defer tracy.end();
  18940 
  18941     const pt = sema.pt;
  18942     const zcu = pt.zcu;
  18943     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  18944     const src = block.nodeOffset(inst_data.src_node);
  18945 
  18946     // Generic poison means this is an untyped anonymous empty struct/array init
  18947     const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse {
  18948         if (is_byref) {
  18949             return sema.uavRef(.empty_tuple);
  18950         } else {
  18951             return .empty_tuple;
  18952         }
  18953     };
  18954 
  18955     const init_ty = if (is_byref) ty: {
  18956         const ptr_ty = ty_operand.optEuBaseType(zcu);
  18957         assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
  18958         switch (ptr_ty.ptrSize(zcu)) {
  18959             // Use a zero-length array for a slice or many-ptr result
  18960             .slice, .many => break :ty try pt.arrayType(.{
  18961                 .len = 0,
  18962                 .child = ptr_ty.childType(zcu).toIntern(),
  18963                 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  18964             }),
  18965             // Just use the child type for a single-pointer or C-pointer result
  18966             .one, .c => {
  18967                 const child = ptr_ty.childType(zcu);
  18968                 if (child.toIntern() == .anyopaque_type) {
  18969                     // ...unless that child is anyopaque, in which case this is equivalent to an untyped init.
  18970                     // `.{}` is an empty tuple.
  18971                     if (is_byref) {
  18972                         return sema.uavRef(.empty_tuple);
  18973                     } else {
  18974                         return .empty_tuple;
  18975                     }
  18976                 }
  18977                 break :ty child;
  18978             },
  18979         }
  18980         if (!ptr_ty.isSlice(zcu)) {
  18981             break :ty ptr_ty.childType(zcu);
  18982         }
  18983         // To make `&.{}` a `[:s]T`, the init should be a `[0:s]T`.
  18984         break :ty try pt.arrayType(.{
  18985             .len = 0,
  18986             .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  18987             .child = ptr_ty.childType(zcu).toIntern(),
  18988         });
  18989     } else ty_operand;
  18990     const obj_ty = init_ty.optEuBaseType(zcu);
  18991 
  18992     const empty_ref = switch (obj_ty.zigTypeTag(zcu)) {
  18993         .@"struct" => try sema.structInitEmpty(block, obj_ty, src, src),
  18994         .array, .vector => try sema.arrayInitEmpty(block, src, obj_ty),
  18995         .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}),
  18996         else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
  18997     };
  18998     const init_ref = try sema.coerce(block, init_ty, empty_ref, src);
  18999 
  19000     if (is_byref) {
  19001         const init_val = (try sema.resolveValue(init_ref)).?;
  19002         return sema.uavRef(init_val.toIntern());
  19003     } else {
  19004         return init_ref;
  19005     }
  19006 }
  19007 
  19008 fn structInitEmpty(
  19009     sema: *Sema,
  19010     block: *Block,
  19011     struct_ty: Type,
  19012     dest_src: LazySrcLoc,
  19013     init_src: LazySrcLoc,
  19014 ) CompileError!Air.Inst.Ref {
  19015     const pt = sema.pt;
  19016     const zcu = pt.zcu;
  19017     const gpa = sema.gpa;
  19018     // This logic must be synchronized with that in `zirStructInit`.
  19019     try struct_ty.resolveFields(pt);
  19020 
  19021     // The init values to use for the struct instance.
  19022     const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(zcu));
  19023     defer gpa.free(field_inits);
  19024     @memset(field_inits, .none);
  19025 
  19026     // Maps field index in the struct declaration to the field index in the initialization expression.
  19027     const field_assign_idxs = try gpa.alloc(?usize, struct_ty.structFieldCount(zcu));
  19028     defer gpa.free(field_assign_idxs);
  19029     @memset(field_assign_idxs, null);
  19030 
  19031     return sema.finishStructInit(block, init_src, dest_src, field_inits, field_assign_idxs, struct_ty, struct_ty, false);
  19032 }
  19033 
  19034 fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref {
  19035     const pt = sema.pt;
  19036     const zcu = pt.zcu;
  19037     const arr_len = obj_ty.arrayLen(zcu);
  19038     if (arr_len != 0) {
  19039         if (obj_ty.zigTypeTag(zcu) == .array) {
  19040             return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len});
  19041         } else {
  19042             return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len});
  19043         }
  19044     }
  19045     return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  19046         .ty = obj_ty.toIntern(),
  19047         .storage = .{ .elems = &.{} },
  19048     } })));
  19049 }
  19050 
  19051 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19052     const pt = sema.pt;
  19053     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19054     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  19055     const field_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  19056     const init_src = block.builtinCallArgSrc(inst_data.src_node, 2);
  19057     const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
  19058     const union_ty = try sema.resolveType(block, ty_src, extra.union_type);
  19059     if (union_ty.zigTypeTag(pt.zcu) != .@"union") {
  19060         return sema.fail(block, ty_src, "expected union type, found '{f}'", .{union_ty.fmt(pt)});
  19061     }
  19062     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .union_field_name });
  19063     const init = try sema.resolveInst(extra.init);
  19064     return sema.unionInit(block, init, init_src, union_ty, ty_src, field_name, field_src);
  19065 }
  19066 
  19067 fn unionInit(
  19068     sema: *Sema,
  19069     block: *Block,
  19070     uncasted_init: Air.Inst.Ref,
  19071     init_src: LazySrcLoc,
  19072     union_ty: Type,
  19073     union_ty_src: LazySrcLoc,
  19074     field_name: InternPool.NullTerminatedString,
  19075     field_src: LazySrcLoc,
  19076 ) CompileError!Air.Inst.Ref {
  19077     const pt = sema.pt;
  19078     const zcu = pt.zcu;
  19079     const ip = &zcu.intern_pool;
  19080     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
  19081     const field_ty: Type = .fromInterned(zcu.typeToUnion(union_ty).?.field_types.get(ip)[field_index]);
  19082     const init = try sema.coerce(block, field_ty, uncasted_init, init_src);
  19083     _ = union_ty_src;
  19084     return unionInitFromEnumTag(sema, block, init_src, union_ty, field_index, init);
  19085 }
  19086 
  19087 fn unionInitFromEnumTag(
  19088     sema: *Sema,
  19089     block: *Block,
  19090     init_src: LazySrcLoc,
  19091     union_ty: Type,
  19092     field_index: u32,
  19093     init: Air.Inst.Ref,
  19094 ) !Air.Inst.Ref {
  19095     const pt = sema.pt;
  19096     const zcu = pt.zcu;
  19097 
  19098     if (try sema.resolveValue(init)) |init_val| {
  19099         const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
  19100         const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
  19101         return Air.internedToRef((try pt.internUnion(.{
  19102             .ty = union_ty.toIntern(),
  19103             .tag = tag_val.toIntern(),
  19104             .val = init_val.toIntern(),
  19105         })));
  19106     }
  19107 
  19108     try sema.requireRuntimeBlock(block, init_src, null);
  19109     return block.addUnionInit(union_ty, field_index, init);
  19110 }
  19111 
  19112 fn zirStructInit(
  19113     sema: *Sema,
  19114     block: *Block,
  19115     inst: Zir.Inst.Index,
  19116     is_ref: bool,
  19117 ) CompileError!Air.Inst.Ref {
  19118     const gpa = sema.gpa;
  19119     const zir_datas = sema.code.instructions.items(.data);
  19120     const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
  19121     const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
  19122     const src = block.nodeOffset(inst_data.src_node);
  19123 
  19124     const pt = sema.pt;
  19125     const zcu = pt.zcu;
  19126     const ip = &zcu.intern_pool;
  19127     const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
  19128     const first_field_type_data = zir_datas[@intFromEnum(first_item.field_type)].pl_node;
  19129     const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
  19130     const result_ty = try sema.resolveTypeOrPoison(block, src, first_field_type_extra.container_type) orelse {
  19131         // The type wasn't actually known, so treat this as an anon struct init.
  19132         return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref);
  19133     };
  19134     const resolved_ty = result_ty.optEuBaseType(zcu);
  19135     try resolved_ty.resolveLayout(pt);
  19136 
  19137     if (resolved_ty.zigTypeTag(zcu) == .@"struct") {
  19138         // This logic must be synchronized with that in `zirStructInitEmpty`.
  19139 
  19140         // Maps field index to field_type index of where it was already initialized.
  19141         // For making sure all fields are accounted for and no fields are duplicated.
  19142         const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(zcu));
  19143         defer gpa.free(found_fields);
  19144 
  19145         // The init values to use for the struct instance.
  19146         const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(zcu));
  19147         defer gpa.free(field_inits);
  19148         @memset(field_inits, .none);
  19149 
  19150         // Maps field index in the struct declaration to the field index in the initialization expression.
  19151         const field_assign_idxs = try gpa.alloc(?usize, resolved_ty.structFieldCount(zcu));
  19152         defer gpa.free(field_assign_idxs);
  19153         @memset(field_assign_idxs, null);
  19154 
  19155         var field_i: u32 = 0;
  19156         var extra_index = extra.end;
  19157 
  19158         const is_packed = resolved_ty.containerLayout(zcu) == .@"packed";
  19159         while (field_i < extra.data.fields_len) : (field_i += 1) {
  19160             const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
  19161             extra_index = item.end;
  19162 
  19163             const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
  19164             const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node });
  19165             const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  19166             const field_name = try ip.getOrPutString(
  19167                 gpa,
  19168                 pt.tid,
  19169                 sema.code.nullTerminatedString(field_type_extra.name_start),
  19170                 .no_embedded_nulls,
  19171             );
  19172             const field_index = if (resolved_ty.isTuple(zcu))
  19173                 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
  19174             else
  19175                 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
  19176             assert(field_inits[field_index] == .none);
  19177             field_assign_idxs[field_index] = field_i;
  19178             found_fields[field_index] = item.data.field_type;
  19179             const uncoerced_init = try sema.resolveInst(item.data.init);
  19180             const field_ty = resolved_ty.fieldType(field_index, zcu);
  19181             field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src);
  19182             if (!is_packed) {
  19183                 try resolved_ty.resolveStructFieldInits(pt);
  19184                 if (try resolved_ty.structFieldValueComptime(pt, field_index)) |default_value| {
  19185                     const init_val = (try sema.resolveValue(field_inits[field_index])) orelse {
  19186                         return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field });
  19187                     };
  19188 
  19189                     if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) {
  19190                         return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
  19191                     }
  19192                 }
  19193             }
  19194         }
  19195 
  19196         return sema.finishStructInit(block, src, src, field_inits, field_assign_idxs, resolved_ty, result_ty, is_ref);
  19197     } else if (resolved_ty.zigTypeTag(zcu) == .@"union") {
  19198         if (extra.data.fields_len != 1) {
  19199             return sema.fail(block, src, "union initialization expects exactly one field", .{});
  19200         }
  19201 
  19202         const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end);
  19203 
  19204         const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
  19205         const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node });
  19206         const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
  19207         const field_name = try ip.getOrPutString(
  19208             gpa,
  19209             pt.tid,
  19210             sema.code.nullTerminatedString(field_type_extra.name_start),
  19211             .no_embedded_nulls,
  19212         );
  19213         const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src);
  19214         const tag_ty = resolved_ty.unionTagTypeHypothetical(zcu);
  19215         const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
  19216         const field_ty: Type = .fromInterned(zcu.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index]);
  19217 
  19218         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  19219             return sema.failWithOwnedErrorMsg(block, msg: {
  19220                 const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{});
  19221                 errdefer msg.destroy(sema.gpa);
  19222 
  19223                 try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{f}' declared here", .{
  19224                     field_name.fmt(ip),
  19225                 });
  19226                 try sema.addDeclaredHereNote(msg, resolved_ty);
  19227                 break :msg msg;
  19228             });
  19229         }
  19230 
  19231         const uncoerced_init_inst = try sema.resolveInst(item.data.init);
  19232         const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);
  19233 
  19234         if (try sema.resolveValue(init_inst)) |val| {
  19235             const struct_val = Value.fromInterned(try pt.internUnion(.{
  19236                 .ty = resolved_ty.toIntern(),
  19237                 .tag = tag_val.toIntern(),
  19238                 .val = val.toIntern(),
  19239             }));
  19240             const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src);
  19241             const final_val = (try sema.resolveValue(final_val_inst)).?;
  19242             return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
  19243         }
  19244 
  19245         if (try resolved_ty.comptimeOnlySema(pt)) {
  19246             return sema.failWithNeededComptime(block, field_src, .{ .comptime_only = .{
  19247                 .ty = resolved_ty,
  19248                 .msg = .union_init,
  19249             } });
  19250         }
  19251 
  19252         try sema.validateRuntimeValue(block, field_src, init_inst);
  19253 
  19254         if (is_ref) {
  19255             const target = zcu.getTarget();
  19256             const alloc_ty = try pt.ptrTypeSema(.{
  19257                 .child = result_ty.toIntern(),
  19258                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19259             });
  19260             const alloc = try block.addTy(.alloc, alloc_ty);
  19261             const base_ptr = try sema.optEuBasePtrInit(block, alloc, src);
  19262             const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true);
  19263             try sema.storePtr(block, src, field_ptr, init_inst);
  19264             if ((try sema.typeHasOnePossibleValue(tag_ty)) == null) {
  19265                 const new_tag = Air.internedToRef(tag_val.toIntern());
  19266                 _ = try block.addBinOp(.set_union_tag, base_ptr, new_tag);
  19267             }
  19268             return sema.makePtrConst(block, alloc);
  19269         }
  19270 
  19271         try sema.requireRuntimeBlock(block, src, null);
  19272         const union_val = try block.addUnionInit(resolved_ty, field_index, init_inst);
  19273         return sema.coerce(block, result_ty, union_val, src);
  19274     }
  19275     unreachable;
  19276 }
  19277 
  19278 fn finishStructInit(
  19279     sema: *Sema,
  19280     block: *Block,
  19281     init_src: LazySrcLoc,
  19282     dest_src: LazySrcLoc,
  19283     field_inits: []Air.Inst.Ref,
  19284     field_assign_idxs: []?usize,
  19285     struct_ty: Type,
  19286     result_ty: Type,
  19287     is_ref: bool,
  19288 ) CompileError!Air.Inst.Ref {
  19289     const pt = sema.pt;
  19290     const zcu = pt.zcu;
  19291     const ip = &zcu.intern_pool;
  19292 
  19293     var root_msg: ?*Zcu.ErrorMsg = null;
  19294     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  19295 
  19296     switch (ip.indexToKey(struct_ty.toIntern())) {
  19297         .tuple_type => |tuple| {
  19298             // We can't get the slices, as the coercion may invalidate them.
  19299             for (0..tuple.types.len) |i| {
  19300                 if (field_inits[i] != .none) {
  19301                     // Coerce the init value to the field type.
  19302                     const field_src = block.src(.{ .init_elem = .{
  19303                         .init_node_offset = init_src.offset.node_offset.x,
  19304                         .elem_index = @intCast(i),
  19305                     } });
  19306                     const field_ty: Type = .fromInterned(tuple.types.get(ip)[i]);
  19307                     field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src);
  19308                     continue;
  19309                 }
  19310 
  19311                 const default_val = tuple.values.get(ip)[i];
  19312 
  19313                 if (default_val == .none) {
  19314                     const template = "missing tuple field with index {d}";
  19315                     if (root_msg) |msg| {
  19316                         try sema.errNote(init_src, msg, template, .{i});
  19317                     } else {
  19318                         root_msg = try sema.errMsg(init_src, template, .{i});
  19319                     }
  19320                 } else {
  19321                     field_inits[i] = Air.internedToRef(default_val);
  19322                 }
  19323             }
  19324         },
  19325         .struct_type => {
  19326             const struct_type = ip.loadStructType(struct_ty.toIntern());
  19327             for (0..struct_type.field_types.len) |i| {
  19328                 if (field_inits[i] != .none) {
  19329                     // Coerce the init value to the field type.
  19330                     const field_src = block.src(.{ .init_elem = .{
  19331                         .init_node_offset = init_src.offset.node_offset.x,
  19332                         .elem_index = @intCast(i),
  19333                     } });
  19334                     const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  19335                     field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src);
  19336                     continue;
  19337                 }
  19338 
  19339                 try struct_ty.resolveStructFieldInits(pt);
  19340 
  19341                 const field_init = struct_type.fieldInit(ip, i);
  19342                 if (field_init == .none) {
  19343                     const field_name = struct_type.field_names.get(ip)[i];
  19344                     const template = "missing struct field: {f}";
  19345                     const args = .{field_name.fmt(ip)};
  19346                     if (root_msg) |msg| {
  19347                         try sema.errNote(init_src, msg, template, args);
  19348                     } else {
  19349                         root_msg = try sema.errMsg(init_src, template, args);
  19350                     }
  19351                 } else {
  19352                     field_inits[i] = Air.internedToRef(field_init);
  19353                 }
  19354             }
  19355         },
  19356         else => unreachable,
  19357     }
  19358 
  19359     if (root_msg) |msg| {
  19360         try sema.addDeclaredHereNote(msg, struct_ty);
  19361         root_msg = null;
  19362         return sema.failWithOwnedErrorMsg(block, msg);
  19363     }
  19364 
  19365     // Find which field forces the expression to be runtime, if any.
  19366     const opt_runtime_index = for (field_inits, field_assign_idxs) |field_init, field_assign| {
  19367         if (!(try sema.isComptimeKnown(field_init))) {
  19368             break field_assign;
  19369         }
  19370     } else null;
  19371 
  19372     const runtime_index = opt_runtime_index orelse {
  19373         const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
  19374         for (elems, field_inits) |*elem, field_init| {
  19375             elem.* = (sema.resolveValue(field_init) catch unreachable).?.toIntern();
  19376         }
  19377         const struct_val = try pt.intern(.{ .aggregate = .{
  19378             .ty = struct_ty.toIntern(),
  19379             .storage = .{ .elems = elems },
  19380         } });
  19381         const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val), init_src);
  19382         const final_val = (try sema.resolveValue(final_val_inst)).?;
  19383         return sema.addConstantMaybeRef(final_val.toIntern(), is_ref);
  19384     };
  19385 
  19386     if (try struct_ty.comptimeOnlySema(pt)) {
  19387         return sema.failWithNeededComptime(block, block.src(.{ .init_elem = .{
  19388             .init_node_offset = init_src.offset.node_offset.x,
  19389             .elem_index = @intCast(runtime_index),
  19390         } }), .{ .comptime_only = .{
  19391             .ty = struct_ty,
  19392             .msg = .struct_init,
  19393         } });
  19394     }
  19395 
  19396     for (field_inits) |field_init| {
  19397         try sema.validateRuntimeValue(block, dest_src, field_init);
  19398     }
  19399 
  19400     if (is_ref) {
  19401         try struct_ty.resolveLayout(pt);
  19402         const target = zcu.getTarget();
  19403         const alloc_ty = try pt.ptrTypeSema(.{
  19404             .child = result_ty.toIntern(),
  19405             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19406         });
  19407         const alloc = try block.addTy(.alloc, alloc_ty);
  19408         const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src);
  19409         for (field_inits, 0..) |field_init, i_usize| {
  19410             const i: u32 = @intCast(i_usize);
  19411             const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, struct_ty);
  19412             try sema.storePtr(block, dest_src, field_ptr, field_init);
  19413         }
  19414 
  19415         return sema.makePtrConst(block, alloc);
  19416     }
  19417 
  19418     try sema.requireRuntimeBlock(block, dest_src, block.src(.{ .init_elem = .{
  19419         .init_node_offset = init_src.offset.node_offset.x,
  19420         .elem_index = @intCast(runtime_index),
  19421     } }));
  19422     try struct_ty.resolveStructFieldInits(pt);
  19423     const struct_val = try block.addAggregateInit(struct_ty, field_inits);
  19424     return sema.coerce(block, result_ty, struct_val, init_src);
  19425 }
  19426 
  19427 fn zirStructInitAnon(
  19428     sema: *Sema,
  19429     block: *Block,
  19430     inst: Zir.Inst.Index,
  19431 ) CompileError!Air.Inst.Ref {
  19432     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19433     const src = block.nodeOffset(inst_data.src_node);
  19434     const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
  19435     return sema.structInitAnon(block, src, inst, .anon_init, extra.data, extra.end, false);
  19436 }
  19437 
  19438 fn structInitAnon(
  19439     sema: *Sema,
  19440     block: *Block,
  19441     src: LazySrcLoc,
  19442     inst: Zir.Inst.Index,
  19443     /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a
  19444     /// generic poison type. In this case, we need to know to interpret the extra data differently.
  19445     comptime kind: enum { anon_init, typed_init },
  19446     extra_data: switch (kind) {
  19447         .anon_init => Zir.Inst.StructInitAnon,
  19448         .typed_init => Zir.Inst.StructInit,
  19449     },
  19450     extra_end: usize,
  19451     is_ref: bool,
  19452 ) CompileError!Air.Inst.Ref {
  19453     const pt = sema.pt;
  19454     const zcu = pt.zcu;
  19455     const gpa = sema.gpa;
  19456     const ip = &zcu.intern_pool;
  19457     const zir_datas = sema.code.instructions.items(.data);
  19458 
  19459     const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
  19460     const values = try sema.arena.alloc(InternPool.Index, types.len);
  19461     const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len);
  19462 
  19463     var any_values = false;
  19464 
  19465     // Find which field forces the expression to be runtime, if any.
  19466     const opt_runtime_index = rs: {
  19467         var runtime_index: ?usize = null;
  19468         var extra_index = extra_end;
  19469         for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| {
  19470             const item = switch (kind) {
  19471                 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
  19472                 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
  19473             };
  19474             extra_index = item.end;
  19475 
  19476             const name = switch (kind) {
  19477                 .anon_init => sema.code.nullTerminatedString(item.data.field_name),
  19478                 .typed_init => name: {
  19479                     // `item.data.field_type` references a `field_type` instruction
  19480                     const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
  19481                     const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index);
  19482                     break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);
  19483                 },
  19484             };
  19485 
  19486             field_name.* = try zcu.intern_pool.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
  19487 
  19488             const init = try sema.resolveInst(item.data.init);
  19489             field_ty.* = sema.typeOf(init).toIntern();
  19490             if (Type.fromInterned(field_ty.*).zigTypeTag(zcu) == .@"opaque") {
  19491                 const msg = msg: {
  19492                     const field_src = block.src(.{ .init_elem = .{
  19493                         .init_node_offset = src.offset.node_offset.x,
  19494                         .elem_index = @intCast(i_usize),
  19495                     } });
  19496                     const msg = try sema.errMsg(field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  19497                     errdefer msg.destroy(sema.gpa);
  19498 
  19499                     try sema.addDeclaredHereNote(msg, .fromInterned(field_ty.*));
  19500                     break :msg msg;
  19501                 };
  19502                 return sema.failWithOwnedErrorMsg(block, msg);
  19503             }
  19504             if (try sema.resolveValue(init)) |init_val| {
  19505                 field_val.* = init_val.toIntern();
  19506                 any_values = true;
  19507             } else {
  19508                 field_val.* = .none;
  19509                 runtime_index = @intCast(i_usize);
  19510             }
  19511         }
  19512         break :rs runtime_index;
  19513     };
  19514 
  19515     // We treat anonymous struct types as reified types, because there are similarities:
  19516     // * They use a form of structural equivalence, which we can easily model using a custom hash
  19517     // * They do not have captures
  19518     // * They immediately have their fields resolved
  19519     // In general, other code should treat anon struct types and reified struct types identically,
  19520     // so there's no point having a separate `InternPool.NamespaceType` field for them.
  19521     const type_hash: u64 = hash: {
  19522         var hasher = std.hash.Wyhash.init(0);
  19523         hasher.update(std.mem.sliceAsBytes(types));
  19524         hasher.update(std.mem.sliceAsBytes(values));
  19525         hasher.update(std.mem.sliceAsBytes(names));
  19526         break :hash hasher.final();
  19527     };
  19528     const tracked_inst = try block.trackZir(inst);
  19529     const struct_ty = switch (try ip.getStructType(gpa, pt.tid, .{
  19530         .layout = .auto,
  19531         .fields_len = extra_data.fields_len,
  19532         .known_non_opv = false,
  19533         .requires_comptime = .unknown,
  19534         .any_comptime_fields = any_values,
  19535         .any_default_inits = any_values,
  19536         .inits_resolved = true,
  19537         .any_aligned_fields = false,
  19538         .key = .{ .reified = .{
  19539             .zir_index = tracked_inst,
  19540             .type_hash = type_hash,
  19541         } },
  19542     }, false)) {
  19543         .wip => |wip| ty: {
  19544             errdefer wip.cancel(ip, pt.tid);
  19545             const type_name = try sema.createTypeName(block, .anon, "struct", inst, wip.index);
  19546             wip.setName(ip, type_name.name, type_name.nav);
  19547 
  19548             const struct_type = ip.loadStructType(wip.index);
  19549 
  19550             for (names, values, 0..) |name, init_val, field_idx| {
  19551                 assert(struct_type.addFieldName(ip, name) == null);
  19552                 if (init_val != .none) struct_type.setFieldComptime(ip, field_idx);
  19553             }
  19554 
  19555             @memcpy(struct_type.field_types.get(ip), types);
  19556             if (any_values) {
  19557                 @memcpy(struct_type.field_inits.get(ip), values);
  19558             }
  19559 
  19560             const new_namespace_index = try pt.createNamespace(.{
  19561                 .parent = block.namespace.toOptional(),
  19562                 .owner_type = wip.index,
  19563                 .file_scope = block.getFileScopeIndex(zcu),
  19564                 .generation = zcu.generation,
  19565             });
  19566             try zcu.comp.queueJob(.{ .resolve_type_fully = wip.index });
  19567             codegen_type: {
  19568                 if (zcu.comp.config.use_llvm) break :codegen_type;
  19569                 if (block.ownerModule().strip) break :codegen_type;
  19570                 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
  19571                 try zcu.comp.queueJob(.{ .link_type = wip.index });
  19572             }
  19573             if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
  19574             break :ty wip.finish(ip, new_namespace_index);
  19575         },
  19576         .existing => |ty| ty,
  19577     };
  19578     try sema.declareDependency(.{ .interned = struct_ty });
  19579     try sema.addTypeReferenceEntry(src, struct_ty);
  19580 
  19581     _ = opt_runtime_index orelse {
  19582         const struct_val = try pt.intern(.{ .aggregate = .{
  19583             .ty = struct_ty,
  19584             .storage = .{ .elems = values },
  19585         } });
  19586         return sema.addConstantMaybeRef(struct_val, is_ref);
  19587     };
  19588 
  19589     if (is_ref) {
  19590         const target = zcu.getTarget();
  19591         const alloc_ty = try pt.ptrTypeSema(.{
  19592             .child = struct_ty,
  19593             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19594         });
  19595         const alloc = try block.addTy(.alloc, alloc_ty);
  19596         var extra_index = extra_end;
  19597         for (types, 0..) |field_ty, i_usize| {
  19598             const i: u32 = @intCast(i_usize);
  19599             const item = switch (kind) {
  19600                 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
  19601                 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
  19602             };
  19603             extra_index = item.end;
  19604 
  19605             const field_ptr_ty = try pt.ptrTypeSema(.{
  19606                 .child = field_ty,
  19607                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19608             });
  19609             if (values[i] == .none) {
  19610                 const init = try sema.resolveInst(item.data.init);
  19611                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  19612                 _ = try block.addBinOp(.store, field_ptr, init);
  19613             }
  19614         }
  19615 
  19616         return sema.makePtrConst(block, alloc);
  19617     }
  19618 
  19619     const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);
  19620     var extra_index = extra_end;
  19621     for (types, 0..) |_, i| {
  19622         const item = switch (kind) {
  19623             .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
  19624             .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
  19625         };
  19626         extra_index = item.end;
  19627         element_refs[i] = try sema.resolveInst(item.data.init);
  19628     }
  19629 
  19630     return block.addAggregateInit(.fromInterned(struct_ty), element_refs);
  19631 }
  19632 
  19633 fn zirArrayInit(
  19634     sema: *Sema,
  19635     block: *Block,
  19636     inst: Zir.Inst.Index,
  19637     is_ref: bool,
  19638 ) CompileError!Air.Inst.Ref {
  19639     const pt = sema.pt;
  19640     const zcu = pt.zcu;
  19641     const gpa = sema.gpa;
  19642     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19643     const src = block.nodeOffset(inst_data.src_node);
  19644 
  19645     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  19646     const args = sema.code.refSlice(extra.end, extra.data.operands_len);
  19647     assert(args.len >= 2); // array_ty + at least one element
  19648 
  19649     const result_ty = try sema.resolveTypeOrPoison(block, src, args[0]) orelse {
  19650         // The type wasn't actually known, so treat this as an anon array init.
  19651         return sema.arrayInitAnon(block, src, args[1..], is_ref);
  19652     };
  19653     const array_ty = result_ty.optEuBaseType(zcu);
  19654     const is_tuple = array_ty.zigTypeTag(zcu) == .@"struct";
  19655     const sentinel_val = array_ty.sentinel(zcu);
  19656 
  19657     var root_msg: ?*Zcu.ErrorMsg = null;
  19658     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  19659 
  19660     const final_len = try sema.usizeCast(block, src, array_ty.arrayLenIncludingSentinel(zcu));
  19661     const resolved_args = try gpa.alloc(Air.Inst.Ref, final_len);
  19662     defer gpa.free(resolved_args);
  19663     for (resolved_args, 0..) |*dest, i| {
  19664         const elem_src = block.src(.{ .init_elem = .{
  19665             .init_node_offset = src.offset.node_offset.x,
  19666             .elem_index = @intCast(i),
  19667         } });
  19668         // Less inits than needed.
  19669         if (i + 2 > args.len) if (is_tuple) {
  19670             const default_val = array_ty.structFieldDefaultValue(i, zcu).toIntern();
  19671             if (default_val == .unreachable_value) {
  19672                 const template = "missing tuple field with index {d}";
  19673                 if (root_msg) |msg| {
  19674                     try sema.errNote(src, msg, template, .{i});
  19675                 } else {
  19676                     root_msg = try sema.errMsg(src, template, .{i});
  19677                 }
  19678             } else {
  19679                 dest.* = Air.internedToRef(default_val);
  19680             }
  19681             continue;
  19682         } else {
  19683             dest.* = Air.internedToRef(sentinel_val.?.toIntern());
  19684             break;
  19685         };
  19686 
  19687         const arg = args[i + 1];
  19688         const resolved_arg = try sema.resolveInst(arg);
  19689         const elem_ty = if (is_tuple)
  19690             array_ty.fieldType(i, zcu)
  19691         else
  19692             array_ty.elemType2(zcu);
  19693         dest.* = try sema.coerce(block, elem_ty, resolved_arg, elem_src);
  19694         if (is_tuple) {
  19695             if (array_ty.structFieldIsComptime(i, zcu))
  19696                 try array_ty.resolveStructFieldInits(pt);
  19697             if (try array_ty.structFieldValueComptime(pt, i)) |field_val| {
  19698                 const init_val = try sema.resolveConstValue(block, elem_src, dest.*, .{ .simple = .stored_to_comptime_field });
  19699                 if (!field_val.eql(init_val, elem_ty, zcu)) {
  19700                     return sema.failWithInvalidComptimeFieldStore(block, elem_src, array_ty, i);
  19701                 }
  19702             }
  19703         }
  19704     }
  19705 
  19706     if (root_msg) |msg| {
  19707         try sema.addDeclaredHereNote(msg, array_ty);
  19708         root_msg = null;
  19709         return sema.failWithOwnedErrorMsg(block, msg);
  19710     }
  19711 
  19712     const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| {
  19713         const comptime_known = try sema.isComptimeKnown(arg);
  19714         if (!comptime_known) break @intCast(i);
  19715     } else null;
  19716 
  19717     _ = opt_runtime_index orelse {
  19718         const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len);
  19719         for (elem_vals, resolved_args) |*val, arg| {
  19720             // We checked that all args are comptime above.
  19721             val.* = (sema.resolveValue(arg) catch unreachable).?.toIntern();
  19722         }
  19723         const arr_val = try pt.intern(.{ .aggregate = .{
  19724             .ty = array_ty.toIntern(),
  19725             .storage = .{ .elems = elem_vals },
  19726         } });
  19727         const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val), src);
  19728         const result_val = (try sema.resolveValue(result_ref)).?;
  19729         return sema.addConstantMaybeRef(result_val.toIntern(), is_ref);
  19730     };
  19731 
  19732     if (is_ref) {
  19733         const target = zcu.getTarget();
  19734         const alloc_ty = try pt.ptrTypeSema(.{
  19735             .child = result_ty.toIntern(),
  19736             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19737         });
  19738         const alloc = try block.addTy(.alloc, alloc_ty);
  19739         const base_ptr = try sema.optEuBasePtrInit(block, alloc, src);
  19740 
  19741         if (is_tuple) {
  19742             for (resolved_args, 0..) |arg, i| {
  19743                 const elem_ptr_ty = try pt.ptrTypeSema(.{
  19744                     .child = array_ty.fieldType(i, zcu).toIntern(),
  19745                     .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19746                 });
  19747                 const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern());
  19748 
  19749                 const index = try pt.intRef(.usize, i);
  19750                 const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref);
  19751                 _ = try block.addBinOp(.store, elem_ptr, arg);
  19752             }
  19753             return sema.makePtrConst(block, alloc);
  19754         }
  19755 
  19756         const elem_ptr_ty = try pt.ptrTypeSema(.{
  19757             .child = array_ty.elemType2(zcu).toIntern(),
  19758             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19759         });
  19760         const elem_ptr_ty_ref = Air.internedToRef(elem_ptr_ty.toIntern());
  19761 
  19762         for (resolved_args, 0..) |arg, i| {
  19763             const index = try pt.intRef(.usize, i);
  19764             const elem_ptr = try block.addPtrElemPtrTypeRef(base_ptr, index, elem_ptr_ty_ref);
  19765             _ = try block.addBinOp(.store, elem_ptr, arg);
  19766         }
  19767         return sema.makePtrConst(block, alloc);
  19768     }
  19769 
  19770     const arr_ref = try block.addAggregateInit(array_ty, resolved_args);
  19771     return sema.coerce(block, result_ty, arr_ref, src);
  19772 }
  19773 
  19774 fn zirArrayInitAnon(
  19775     sema: *Sema,
  19776     block: *Block,
  19777     inst: Zir.Inst.Index,
  19778 ) CompileError!Air.Inst.Ref {
  19779     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19780     const src = block.nodeOffset(inst_data.src_node);
  19781     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
  19782     const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
  19783     return sema.arrayInitAnon(block, src, operands, false);
  19784 }
  19785 
  19786 fn arrayInitAnon(
  19787     sema: *Sema,
  19788     block: *Block,
  19789     src: LazySrcLoc,
  19790     operands: []const Zir.Inst.Ref,
  19791     is_ref: bool,
  19792 ) CompileError!Air.Inst.Ref {
  19793     const pt = sema.pt;
  19794     const zcu = pt.zcu;
  19795     const gpa = sema.gpa;
  19796     const ip = &zcu.intern_pool;
  19797 
  19798     const types = try sema.arena.alloc(InternPool.Index, operands.len);
  19799     const values = try sema.arena.alloc(InternPool.Index, operands.len);
  19800 
  19801     const opt_runtime_src = rs: {
  19802         var runtime_src: ?LazySrcLoc = null;
  19803         for (operands, 0..) |operand, i| {
  19804             const operand_src = src; // TODO better source location
  19805             const elem = try sema.resolveInst(operand);
  19806             types[i] = sema.typeOf(elem).toIntern();
  19807             if (Type.fromInterned(types[i]).zigTypeTag(zcu) == .@"opaque") {
  19808                 const msg = msg: {
  19809                     const msg = try sema.errMsg(operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  19810                     errdefer msg.destroy(gpa);
  19811 
  19812                     try sema.addDeclaredHereNote(msg, .fromInterned(types[i]));
  19813                     break :msg msg;
  19814                 };
  19815                 return sema.failWithOwnedErrorMsg(block, msg);
  19816             }
  19817             if (try sema.resolveValue(elem)) |val| {
  19818                 values[i] = val.toIntern();
  19819             } else {
  19820                 values[i] = .none;
  19821                 runtime_src = operand_src;
  19822             }
  19823         }
  19824         break :rs runtime_src;
  19825     };
  19826 
  19827     const tuple_ty = try ip.getTupleType(gpa, pt.tid, .{
  19828         .types = types,
  19829         .values = values,
  19830     });
  19831 
  19832     const runtime_src = opt_runtime_src orelse {
  19833         const tuple_val = try pt.intern(.{ .aggregate = .{
  19834             .ty = tuple_ty,
  19835             .storage = .{ .elems = values },
  19836         } });
  19837         return sema.addConstantMaybeRef(tuple_val, is_ref);
  19838     };
  19839 
  19840     try sema.requireRuntimeBlock(block, src, runtime_src);
  19841 
  19842     if (is_ref) {
  19843         const target = sema.pt.zcu.getTarget();
  19844         const alloc_ty = try pt.ptrTypeSema(.{
  19845             .child = tuple_ty,
  19846             .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19847         });
  19848         const alloc = try block.addTy(.alloc, alloc_ty);
  19849         for (operands, 0..) |operand, i_usize| {
  19850             const i: u32 = @intCast(i_usize);
  19851             const field_ptr_ty = try pt.ptrTypeSema(.{
  19852                 .child = types[i],
  19853                 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
  19854             });
  19855             if (values[i] == .none) {
  19856                 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
  19857                 _ = try block.addBinOp(.store, field_ptr, try sema.resolveInst(operand));
  19858             }
  19859         }
  19860 
  19861         return sema.makePtrConst(block, alloc);
  19862     }
  19863 
  19864     const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  19865     for (operands, 0..) |operand, i| {
  19866         element_refs[i] = try sema.resolveInst(operand);
  19867     }
  19868 
  19869     return block.addAggregateInit(.fromInterned(tuple_ty), element_refs);
  19870 }
  19871 
  19872 fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.Inst.Ref {
  19873     return if (is_ref) sema.uavRef(val) else Air.internedToRef(val);
  19874 }
  19875 
  19876 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19877     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19878     const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
  19879     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  19880     const field_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  19881     const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
  19882     const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name });
  19883     return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
  19884 }
  19885 
  19886 fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19887     const pt = sema.pt;
  19888     const zcu = pt.zcu;
  19889     const ip = &zcu.intern_pool;
  19890     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  19891     const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
  19892     const ty_src = block.nodeOffset(inst_data.src_node);
  19893     const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node });
  19894     const wrapped_aggregate_ty = try sema.resolveTypeOrPoison(block, ty_src, extra.container_type) orelse return .generic_poison_type;
  19895     const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu);
  19896     const zir_field_name = sema.code.nullTerminatedString(extra.name_start);
  19897     const field_name = try ip.getOrPutString(sema.gpa, pt.tid, zir_field_name, .no_embedded_nulls);
  19898     return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
  19899 }
  19900 
  19901 fn fieldType(
  19902     sema: *Sema,
  19903     block: *Block,
  19904     aggregate_ty: Type,
  19905     field_name: InternPool.NullTerminatedString,
  19906     field_src: LazySrcLoc,
  19907     ty_src: LazySrcLoc,
  19908 ) CompileError!Air.Inst.Ref {
  19909     const pt = sema.pt;
  19910     const zcu = pt.zcu;
  19911     const ip = &zcu.intern_pool;
  19912     var cur_ty = aggregate_ty;
  19913     while (true) {
  19914         try cur_ty.resolveFields(pt);
  19915         switch (cur_ty.zigTypeTag(zcu)) {
  19916             .@"struct" => switch (ip.indexToKey(cur_ty.toIntern())) {
  19917                 .tuple_type => |tuple| {
  19918                     const field_index = try sema.tupleFieldIndex(block, cur_ty, field_name, field_src);
  19919                     return Air.internedToRef(tuple.types.get(ip)[field_index]);
  19920                 },
  19921                 .struct_type => {
  19922                     const struct_type = ip.loadStructType(cur_ty.toIntern());
  19923                     const field_index = struct_type.nameIndex(ip, field_name) orelse
  19924                         return sema.failWithBadStructFieldAccess(block, cur_ty, struct_type, field_src, field_name);
  19925                     const field_ty = struct_type.field_types.get(ip)[field_index];
  19926                     return Air.internedToRef(field_ty);
  19927                 },
  19928                 else => unreachable,
  19929             },
  19930             .@"union" => {
  19931                 const union_obj = zcu.typeToUnion(cur_ty).?;
  19932                 const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse
  19933                     return sema.failWithBadUnionFieldAccess(block, cur_ty, union_obj, field_src, field_name);
  19934                 const field_ty = union_obj.field_types.get(ip)[field_index];
  19935                 return Air.internedToRef(field_ty);
  19936             },
  19937             .optional => {
  19938                 // Struct/array init through optional requires the child type to not be a pointer.
  19939                 // If the child of .optional is a pointer it'll error on the next loop.
  19940                 cur_ty = .fromInterned(ip.indexToKey(cur_ty.toIntern()).opt_type);
  19941                 continue;
  19942             },
  19943             .error_union => {
  19944                 cur_ty = cur_ty.errorUnionPayload(zcu);
  19945                 continue;
  19946             },
  19947             else => {},
  19948         }
  19949         return sema.fail(block, ty_src, "expected struct or union; found '{f}'", .{
  19950             cur_ty.fmt(pt),
  19951         });
  19952     }
  19953 }
  19954 
  19955 fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  19956     return sema.getErrorReturnTrace(block);
  19957 }
  19958 
  19959 fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
  19960     const pt = sema.pt;
  19961     const zcu = pt.zcu;
  19962     const ip = &zcu.intern_pool;
  19963     const stack_trace_ty = try sema.getBuiltinType(block.nodeOffset(.zero), .StackTrace);
  19964     try stack_trace_ty.resolveFields(pt);
  19965     const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
  19966     const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern());
  19967 
  19968     switch (sema.owner.unwrap()) {
  19969         .func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) {
  19970             return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
  19971         },
  19972         .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
  19973     }
  19974     return Air.internedToRef(try pt.intern(.{ .opt = .{
  19975         .ty = opt_ptr_stack_trace_ty.toIntern(),
  19976         .val = .none,
  19977     } }));
  19978 }
  19979 
  19980 fn zirFrame(
  19981     sema: *Sema,
  19982     block: *Block,
  19983     extended: Zir.Inst.Extended.InstData,
  19984 ) CompileError!Air.Inst.Ref {
  19985     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  19986     const src = block.nodeOffset(src_node);
  19987     return sema.failWithUseOfAsync(block, src);
  19988 }
  19989 
  19990 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  19991     const zcu = sema.pt.zcu;
  19992     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  19993     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  19994     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
  19995     if (ty.isNoReturn(zcu)) {
  19996         return sema.fail(block, operand_src, "no align available for type '{f}'", .{ty.fmt(sema.pt)});
  19997     }
  19998     const val = try ty.lazyAbiAlignment(sema.pt);
  19999     return Air.internedToRef(val.toIntern());
  20000 }
  20001 
  20002 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20003     const pt = sema.pt;
  20004     const zcu = pt.zcu;
  20005     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20006     const src = block.nodeOffset(inst_data.src_node);
  20007     const operand = try sema.resolveInst(inst_data.operand);
  20008     const operand_ty = sema.typeOf(operand);
  20009     const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
  20010     const operand_scalar_ty = operand_ty.scalarType(zcu);
  20011     if (operand_scalar_ty.toIntern() != .bool_type) {
  20012         return sema.fail(block, src, "expected 'bool', found '{t}'", .{operand_scalar_ty.zigTypeTag(zcu)});
  20013     }
  20014     const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;
  20015     const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1;
  20016     if (try sema.resolveValue(operand)) |val| {
  20017         if (!is_vector) {
  20018             return if (val.isUndef(zcu)) .undef_u1 else if (val.toBool()) .one_u1 else .zero_u1;
  20019         }
  20020         if (val.isUndef(zcu)) return pt.undefRef(dest_ty);
  20021         const new_elems = try sema.arena.alloc(InternPool.Index, len);
  20022         for (new_elems, 0..) |*new_elem, i| {
  20023             const old_elem = try val.elemValue(pt, i);
  20024             new_elem.* = if (old_elem.isUndef(zcu))
  20025                 .undef_u1
  20026             else if (old_elem.toBool())
  20027                 .one_u1
  20028             else
  20029                 .zero_u1;
  20030         }
  20031         return Air.internedToRef(try pt.intern(.{ .aggregate = .{
  20032             .ty = dest_ty.toIntern(),
  20033             .storage = .{ .elems = new_elems },
  20034         } }));
  20035     }
  20036     return block.addBitCast(dest_ty, operand);
  20037 }
  20038 
  20039 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20040     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20041     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20042     const uncoerced_operand = try sema.resolveInst(inst_data.operand);
  20043     const operand = try sema.coerce(block, .anyerror, uncoerced_operand, operand_src);
  20044 
  20045     if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
  20046         const err_name = sema.pt.zcu.intern_pool.indexToKey(val.toIntern()).err.name;
  20047         return sema.addNullTerminatedStrLit(err_name);
  20048     }
  20049 
  20050     // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass
  20051     // might be able to resolve the result at compile time.
  20052     return block.addUnOp(.error_name, operand);
  20053 }
  20054 
  20055 fn zirAbs(
  20056     sema: *Sema,
  20057     block: *Block,
  20058     inst: Zir.Inst.Index,
  20059 ) CompileError!Air.Inst.Ref {
  20060     const pt = sema.pt;
  20061     const zcu = pt.zcu;
  20062     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20063     const operand = try sema.resolveInst(inst_data.operand);
  20064     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20065     const operand_ty = sema.typeOf(operand);
  20066     const scalar_ty = operand_ty.scalarType(zcu);
  20067 
  20068     const result_ty = switch (scalar_ty.zigTypeTag(zcu)) {
  20069         .comptime_float, .float, .comptime_int => operand_ty,
  20070         .int => if (scalar_ty.isSignedInt(zcu)) try operand_ty.toUnsigned(pt) else return operand,
  20071         else => return sema.fail(
  20072             block,
  20073             operand_src,
  20074             "expected integer, float, or vector of either integers or floats, found '{f}'",
  20075             .{operand_ty.fmt(pt)},
  20076         ),
  20077     };
  20078 
  20079     return (try sema.maybeConstantUnaryMath(operand, result_ty, Value.abs)) orelse {
  20080         try sema.requireRuntimeBlock(block, operand_src, null);
  20081         return block.addTyOp(.abs, result_ty, operand);
  20082     };
  20083 }
  20084 
  20085 fn maybeConstantUnaryMath(
  20086     sema: *Sema,
  20087     operand: Air.Inst.Ref,
  20088     result_ty: Type,
  20089     comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value,
  20090 ) CompileError!?Air.Inst.Ref {
  20091     const pt = sema.pt;
  20092     const zcu = pt.zcu;
  20093     switch (result_ty.zigTypeTag(zcu)) {
  20094         .vector => if (try sema.resolveValue(operand)) |val| {
  20095             const scalar_ty = result_ty.scalarType(zcu);
  20096             const vec_len = result_ty.vectorLen(zcu);
  20097             if (val.isUndef(zcu))
  20098                 return try pt.undefRef(result_ty);
  20099 
  20100             const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  20101             for (elems, 0..) |*elem, i| {
  20102                 const elem_val = try val.elemValue(pt, i);
  20103                 elem.* = (try eval(elem_val, scalar_ty, sema.arena, pt)).toIntern();
  20104             }
  20105             return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  20106                 .ty = result_ty.toIntern(),
  20107                 .storage = .{ .elems = elems },
  20108             } })));
  20109         },
  20110         else => if (try sema.resolveValue(operand)) |operand_val| {
  20111             if (operand_val.isUndef(zcu))
  20112                 return try pt.undefRef(result_ty);
  20113             const result_val = try eval(operand_val, result_ty, sema.arena, pt);
  20114             return Air.internedToRef(result_val.toIntern());
  20115         },
  20116     }
  20117     return null;
  20118 }
  20119 
  20120 fn zirUnaryMath(
  20121     sema: *Sema,
  20122     block: *Block,
  20123     inst: Zir.Inst.Index,
  20124     air_tag: Air.Inst.Tag,
  20125     comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value,
  20126 ) CompileError!Air.Inst.Ref {
  20127     const tracy = trace(@src());
  20128     defer tracy.end();
  20129 
  20130     const pt = sema.pt;
  20131     const zcu = pt.zcu;
  20132     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20133     const operand = try sema.resolveInst(inst_data.operand);
  20134     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20135     const operand_ty = sema.typeOf(operand);
  20136     const scalar_ty = operand_ty.scalarType(zcu);
  20137 
  20138     switch (scalar_ty.zigTypeTag(zcu)) {
  20139         .comptime_float, .float => {},
  20140         else => return sema.fail(
  20141             block,
  20142             operand_src,
  20143             "expected vector of floats or float type, found '{f}'",
  20144             .{operand_ty.fmt(pt)},
  20145         ),
  20146     }
  20147 
  20148     return (try sema.maybeConstantUnaryMath(operand, operand_ty, eval)) orelse {
  20149         try sema.requireRuntimeBlock(block, operand_src, null);
  20150         return block.addUnOp(air_tag, operand);
  20151     };
  20152 }
  20153 
  20154 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  20155     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  20156     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  20157     const src = block.nodeOffset(inst_data.src_node);
  20158     const operand = try sema.resolveInst(inst_data.operand);
  20159     const operand_ty = sema.typeOf(operand);
  20160     const pt = sema.pt;
  20161     const zcu = pt.zcu;
  20162     const ip = &zcu.intern_pool;
  20163     try operand_ty.resolveLayout(pt);
  20164     const enum_ty = switch (operand_ty.zigTypeTag(zcu)) {
  20165         .enum_literal => {
  20166             const val = (try sema.resolveDefinedValue(block, operand_src, operand)).?;
  20167             const tag_name = ip.indexToKey(val.toIntern()).enum_literal;
  20168             return sema.addNullTerminatedStrLit(tag_name);
  20169         },
  20170         .@"enum" => operand_ty,
  20171         .@"union" => operand_ty.unionTagType(zcu) orelse
  20172             return sema.fail(block, src, "union '{f}' is untagged", .{operand_ty.fmt(pt)}),
  20173         else => return sema.fail(block, operand_src, "expected enum or union; found '{f}'", .{
  20174             operand_ty.fmt(pt),
  20175         }),
  20176     };
  20177     if (enum_ty.enumFieldCount(zcu) == 0) {
  20178         // TODO I don't think this is the correct way to handle this but
  20179         // it prevents a crash.
  20180         // https://github.com/ziglang/zig/issues/15909
  20181         return sema.fail(block, operand_src, "cannot get @tagName of empty enum '{f}'", .{
  20182             enum_ty.fmt(pt),
  20183         });
  20184     }
  20185     const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
  20186     if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
  20187         const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse {
  20188             const msg = msg: {
  20189                 const msg = try sema.errMsg(src, "no field with value '{f}' in enum '{f}'", .{
  20190                     val.fmtValueSema(pt, sema), enum_ty.fmt(pt),
  20191                 });
  20192                 errdefer msg.destroy(sema.gpa);
  20193                 try sema.errNote(enum_ty.srcLoc(zcu), msg, "declared here", .{});
  20194                 break :msg msg;
  20195             };
  20196             return sema.failWithOwnedErrorMsg(block, msg);
  20197         };
  20198         // TODO: write something like getCoercedInts to avoid needing to dupe
  20199         const field_name = enum_ty.enumFieldName(field_index, zcu);
  20200         return sema.addNullTerminatedStrLit(field_name);
  20201     }
  20202     try sema.requireRuntimeBlock(block, src, operand_src);
  20203     if (block.wantSafety() and zcu.backendSupportsFeature(.is_named_enum_value)) {
  20204         const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
  20205         try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
  20206     }
  20207     // In case the value is runtime-known, we have an AIR instruction for this instead
  20208     // of trying to lower it in Sema because an optimization pass may result in the operand
  20209     // being comptime-known, which would let us elide the `tag_name` AIR instruction.
  20210     return block.addUnOp(.tag_name, casted_operand);
  20211 }
  20212 
  20213 fn zirReify(
  20214     sema: *Sema,
  20215     block: *Block,
  20216     extended: Zir.Inst.Extended.InstData,
  20217     inst: Zir.Inst.Index,
  20218 ) CompileError!Air.Inst.Ref {
  20219     const pt = sema.pt;
  20220     const zcu = pt.zcu;
  20221     const gpa = sema.gpa;
  20222     const ip = &zcu.intern_pool;
  20223     const name_strategy: Zir.Inst.NameStrategy = @enumFromInt(extended.small);
  20224     const extra = sema.code.extraData(Zir.Inst.Reify, extended.operand).data;
  20225     const tracked_inst = try block.trackZir(inst);
  20226     const src: LazySrcLoc = .{
  20227         .base_node_inst = tracked_inst,
  20228         .offset = LazySrcLoc.Offset.nodeOffset(.zero),
  20229     };
  20230     const operand_src: LazySrcLoc = .{
  20231         .base_node_inst = tracked_inst,
  20232         .offset = .{
  20233             .node_offset_builtin_call_arg = .{
  20234                 .builtin_call_node = .zero, // `tracked_inst` is precisely the `reify` instruction, so offset is 0
  20235                 .arg_index = 0,
  20236             },
  20237         },
  20238     };
  20239     const type_info_ty = try sema.getBuiltinType(src, .Type);
  20240     const uncasted_operand = try sema.resolveInst(extra.operand);
  20241     const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
  20242     const val = try sema.resolveConstDefinedValue(block, operand_src, type_info, .{ .simple = .operand_Type });
  20243     const union_val = ip.indexToKey(val.toIntern()).un;
  20244     if (try sema.anyUndef(block, operand_src, Value.fromInterned(union_val.val))) {
  20245         return sema.failWithUseOfUndef(block, operand_src);
  20246     }
  20247     const tag_index = type_info_ty.unionTagFieldIndex(Value.fromInterned(union_val.tag), zcu).?;
  20248     switch (@as(std.builtin.TypeId, @enumFromInt(tag_index))) {
  20249         .type => return .type_type,
  20250         .void => return .void_type,
  20251         .bool => return .bool_type,
  20252         .noreturn => return .noreturn_type,
  20253         .comptime_float => return .comptime_float_type,
  20254         .comptime_int => return .comptime_int_type,
  20255         .undefined => return .undefined_type,
  20256         .null => return .null_type,
  20257         .@"anyframe" => return sema.failWithUseOfAsync(block, src),
  20258         .enum_literal => return .enum_literal_type,
  20259         .int => {
  20260             const int = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Int);
  20261             const ty = try pt.intType(int.signedness, int.bits);
  20262             return Air.internedToRef(ty.toIntern());
  20263         },
  20264         .vector => {
  20265             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20266             const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20267                 ip,
  20268                 try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls),
  20269             ).?);
  20270             const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20271                 ip,
  20272                 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls),
  20273             ).?);
  20274 
  20275             const len: u32 = @intCast(try len_val.toUnsignedIntSema(pt));
  20276             const child_ty = child_val.toType();
  20277 
  20278             try sema.checkVectorElemType(block, src, child_ty);
  20279 
  20280             const ty = try pt.vectorType(.{
  20281                 .len = len,
  20282                 .child = child_ty.toIntern(),
  20283             });
  20284             return Air.internedToRef(ty.toIntern());
  20285         },
  20286         .float => {
  20287             const float = try sema.interpretBuiltinType(block, operand_src, .fromInterned(union_val.val), std.builtin.Type.Float);
  20288 
  20289             const ty: Type = switch (float.bits) {
  20290                 16 => .f16,
  20291                 32 => .f32,
  20292                 64 => .f64,
  20293                 80 => .f80,
  20294                 128 => .f128,
  20295                 else => return sema.fail(block, src, "{d}-bit float unsupported", .{float.bits}),
  20296             };
  20297             return Air.internedToRef(ty.toIntern());
  20298         },
  20299         .pointer => {
  20300             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20301             const size_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20302                 ip,
  20303                 try ip.getOrPutString(gpa, pt.tid, "size", .no_embedded_nulls),
  20304             ).?);
  20305             const is_const_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20306                 ip,
  20307                 try ip.getOrPutString(gpa, pt.tid, "is_const", .no_embedded_nulls),
  20308             ).?);
  20309             const is_volatile_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20310                 ip,
  20311                 try ip.getOrPutString(gpa, pt.tid, "is_volatile", .no_embedded_nulls),
  20312             ).?);
  20313             const alignment_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20314                 ip,
  20315                 try ip.getOrPutString(gpa, pt.tid, "alignment", .no_embedded_nulls),
  20316             ).?);
  20317             const address_space_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20318                 ip,
  20319                 try ip.getOrPutString(gpa, pt.tid, "address_space", .no_embedded_nulls),
  20320             ).?);
  20321             const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20322                 ip,
  20323                 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls),
  20324             ).?);
  20325             const is_allowzero_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20326                 ip,
  20327                 try ip.getOrPutString(gpa, pt.tid, "is_allowzero", .no_embedded_nulls),
  20328             ).?);
  20329             const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20330                 ip,
  20331                 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls),
  20332             ).?);
  20333 
  20334             if (!try sema.intFitsInType(alignment_val, align_ty, null)) {
  20335                 return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)});
  20336             }
  20337             const alignment_val_int = try alignment_val.toUnsignedIntSema(pt);
  20338             const abi_align = try sema.validateAlign(block, src, alignment_val_int);
  20339 
  20340             const elem_ty = child_val.toType();
  20341             if (abi_align != .none) {
  20342                 try elem_ty.resolveLayout(pt);
  20343             }
  20344 
  20345             const ptr_size = try sema.interpretBuiltinType(block, operand_src, size_val, std.builtin.Type.Pointer.Size);
  20346 
  20347             const actual_sentinel: InternPool.Index = s: {
  20348                 if (!sentinel_val.isNull(zcu)) {
  20349                     if (ptr_size == .one or ptr_size == .c) {
  20350                         return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{});
  20351                     }
  20352                     const sentinel_ptr_val = sentinel_val.optionalValue(zcu).?;
  20353                     const ptr_ty = try pt.singleMutPtrType(elem_ty);
  20354                     const sent_val = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
  20355                     try sema.checkSentinelType(block, src, elem_ty);
  20356                     break :s sent_val.toIntern();
  20357                 }
  20358                 break :s .none;
  20359             };
  20360 
  20361             if (elem_ty.zigTypeTag(zcu) == .noreturn) {
  20362                 return sema.fail(block, src, "pointer to noreturn not allowed", .{});
  20363             } else if (elem_ty.zigTypeTag(zcu) == .@"fn") {
  20364                 if (ptr_size != .one) {
  20365                     return sema.fail(block, src, "function pointers must be single pointers", .{});
  20366                 }
  20367             } else if (ptr_size == .many and elem_ty.zigTypeTag(zcu) == .@"opaque") {
  20368                 return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
  20369             } else if (ptr_size == .c) {
  20370                 if (!try sema.validateExternType(elem_ty, .other)) {
  20371                     const msg = msg: {
  20372                         const msg = try sema.errMsg(src, "C pointers cannot point to non-C-ABI-compatible type '{f}'", .{elem_ty.fmt(pt)});
  20373                         errdefer msg.destroy(gpa);
  20374 
  20375                         try sema.explainWhyTypeIsNotExtern(msg, src, elem_ty, .other);
  20376 
  20377                         try sema.addDeclaredHereNote(msg, elem_ty);
  20378                         break :msg msg;
  20379                     };
  20380                     return sema.failWithOwnedErrorMsg(block, msg);
  20381                 }
  20382                 if (elem_ty.zigTypeTag(zcu) == .@"opaque") {
  20383                     return sema.fail(block, src, "C pointers cannot point to opaque types", .{});
  20384                 }
  20385             }
  20386 
  20387             const ty = try pt.ptrTypeSema(.{
  20388                 .child = elem_ty.toIntern(),
  20389                 .sentinel = actual_sentinel,
  20390                 .flags = .{
  20391                     .size = ptr_size,
  20392                     .is_const = is_const_val.toBool(),
  20393                     .is_volatile = is_volatile_val.toBool(),
  20394                     .alignment = abi_align,
  20395                     .address_space = try sema.interpretBuiltinType(block, operand_src, address_space_val, std.builtin.AddressSpace),
  20396                     .is_allowzero = is_allowzero_val.toBool(),
  20397                 },
  20398             });
  20399             return Air.internedToRef(ty.toIntern());
  20400         },
  20401         .array => {
  20402             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20403             const len_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20404                 ip,
  20405                 try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls),
  20406             ).?);
  20407             const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20408                 ip,
  20409                 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls),
  20410             ).?);
  20411             const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20412                 ip,
  20413                 try ip.getOrPutString(gpa, pt.tid, "sentinel_ptr", .no_embedded_nulls),
  20414             ).?);
  20415 
  20416             const len = try len_val.toUnsignedIntSema(pt);
  20417             const child_ty = child_val.toType();
  20418             const sentinel = if (sentinel_val.optionalValue(zcu)) |p| blk: {
  20419                 const ptr_ty = try pt.singleMutPtrType(child_ty);
  20420                 try sema.checkSentinelType(block, src, child_ty);
  20421                 break :blk (try sema.pointerDeref(block, src, p, ptr_ty)).?;
  20422             } else null;
  20423 
  20424             const ty = try pt.arrayType(.{
  20425                 .len = len,
  20426                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  20427                 .child = child_ty.toIntern(),
  20428             });
  20429             return Air.internedToRef(ty.toIntern());
  20430         },
  20431         .optional => {
  20432             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20433             const child_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20434                 ip,
  20435                 try ip.getOrPutString(gpa, pt.tid, "child", .no_embedded_nulls),
  20436             ).?);
  20437 
  20438             const child_ty = child_val.toType();
  20439 
  20440             const ty = try pt.optionalType(child_ty.toIntern());
  20441             return Air.internedToRef(ty.toIntern());
  20442         },
  20443         .error_union => {
  20444             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20445             const error_set_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20446                 ip,
  20447                 try ip.getOrPutString(gpa, pt.tid, "error_set", .no_embedded_nulls),
  20448             ).?);
  20449             const payload_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20450                 ip,
  20451                 try ip.getOrPutString(gpa, pt.tid, "payload", .no_embedded_nulls),
  20452             ).?);
  20453 
  20454             const error_set_ty = error_set_val.toType();
  20455             const payload_ty = payload_val.toType();
  20456 
  20457             if (error_set_ty.zigTypeTag(zcu) != .error_set) {
  20458                 return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{});
  20459             }
  20460 
  20461             const ty = try pt.errorUnionType(error_set_ty, payload_ty);
  20462             return Air.internedToRef(ty.toIntern());
  20463         },
  20464         .error_set => {
  20465             const payload_val = Value.fromInterned(union_val.val).optionalValue(zcu) orelse
  20466                 return .anyerror_type;
  20467 
  20468             const names_val = try sema.derefSliceAsArray(block, src, payload_val, .{ .simple = .error_set_contents });
  20469 
  20470             const len = try sema.usizeCast(block, src, names_val.typeOf(zcu).arrayLen(zcu));
  20471             var names: InferredErrorSet.NameMap = .{};
  20472             try names.ensureUnusedCapacity(sema.arena, len);
  20473             for (0..len) |i| {
  20474                 const elem_val = try names_val.elemValue(pt, i);
  20475                 const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern()));
  20476                 const name_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex(
  20477                     ip,
  20478                     try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls),
  20479                 ).?);
  20480 
  20481                 const name = try sema.sliceToIpString(block, src, name_val, .{ .simple = .error_set_contents });
  20482                 _ = try pt.getErrorValue(name);
  20483                 const gop = names.getOrPutAssumeCapacity(name);
  20484                 if (gop.found_existing) {
  20485                     return sema.fail(block, src, "duplicate error '{f}'", .{
  20486                         name.fmt(ip),
  20487                     });
  20488                 }
  20489             }
  20490 
  20491             const ty = try pt.errorSetFromUnsortedNames(names.keys());
  20492             return Air.internedToRef(ty.toIntern());
  20493         },
  20494         .@"struct" => {
  20495             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20496             const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20497                 ip,
  20498                 try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls),
  20499             ).?);
  20500             const backing_integer_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20501                 ip,
  20502                 try ip.getOrPutString(gpa, pt.tid, "backing_integer", .no_embedded_nulls),
  20503             ).?);
  20504             const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20505                 ip,
  20506                 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls),
  20507             ).?);
  20508             const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20509                 ip,
  20510                 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls),
  20511             ).?);
  20512             const is_tuple_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20513                 ip,
  20514                 try ip.getOrPutString(gpa, pt.tid, "is_tuple", .no_embedded_nulls),
  20515             ).?);
  20516 
  20517             const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout);
  20518 
  20519             // Decls
  20520             if (try decls_val.sliceLen(pt) > 0) {
  20521                 return sema.fail(block, src, "reified structs must have no decls", .{});
  20522             }
  20523 
  20524             if (layout != .@"packed" and !backing_integer_val.isNull(zcu)) {
  20525                 return sema.fail(block, src, "non-packed struct does not support backing integer type", .{});
  20526             }
  20527 
  20528             const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .struct_fields });
  20529 
  20530             if (is_tuple_val.toBool()) {
  20531                 switch (layout) {
  20532                     .@"extern" => return sema.fail(block, src, "extern tuples are not supported", .{}),
  20533                     .@"packed" => return sema.fail(block, src, "packed tuples are not supported", .{}),
  20534                     .auto => {},
  20535                 }
  20536                 return sema.reifyTuple(block, src, fields_arr);
  20537             } else {
  20538                 return sema.reifyStruct(block, inst, src, layout, backing_integer_val, fields_arr, name_strategy);
  20539             }
  20540         },
  20541         .@"enum" => {
  20542             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20543             const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20544                 ip,
  20545                 try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls),
  20546             ).?);
  20547             const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20548                 ip,
  20549                 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls),
  20550             ).?);
  20551             const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20552                 ip,
  20553                 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls),
  20554             ).?);
  20555             const is_exhaustive_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20556                 ip,
  20557                 try ip.getOrPutString(gpa, pt.tid, "is_exhaustive", .no_embedded_nulls),
  20558             ).?);
  20559 
  20560             if (try decls_val.sliceLen(pt) > 0) {
  20561                 return sema.fail(block, src, "reified enums must have no decls", .{});
  20562             }
  20563 
  20564             const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .enum_fields });
  20565 
  20566             return sema.reifyEnum(block, inst, src, tag_type_val.toType(), is_exhaustive_val.toBool(), fields_arr, name_strategy);
  20567         },
  20568         .@"opaque" => {
  20569             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20570             const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20571                 ip,
  20572                 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls),
  20573             ).?);
  20574 
  20575             // Decls
  20576             if (try decls_val.sliceLen(pt) > 0) {
  20577                 return sema.fail(block, src, "reified opaque must have no decls", .{});
  20578             }
  20579 
  20580             const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, .{
  20581                 .key = .{ .reified = .{
  20582                     .zir_index = try block.trackZir(inst),
  20583                 } },
  20584             })) {
  20585                 .existing => |ty| {
  20586                     try sema.addTypeReferenceEntry(src, ty);
  20587                     return Air.internedToRef(ty);
  20588                 },
  20589                 .wip => |wip| wip,
  20590             };
  20591             errdefer wip_ty.cancel(ip, pt.tid);
  20592 
  20593             const type_name = try sema.createTypeName(
  20594                 block,
  20595                 name_strategy,
  20596                 "opaque",
  20597                 inst,
  20598                 wip_ty.index,
  20599             );
  20600             wip_ty.setName(ip, type_name.name, type_name.nav);
  20601 
  20602             const new_namespace_index = try pt.createNamespace(.{
  20603                 .parent = block.namespace.toOptional(),
  20604                 .owner_type = wip_ty.index,
  20605                 .file_scope = block.getFileScopeIndex(zcu),
  20606                 .generation = zcu.generation,
  20607             });
  20608 
  20609             try sema.addTypeReferenceEntry(src, wip_ty.index);
  20610             if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
  20611             return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
  20612         },
  20613         .@"union" => {
  20614             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20615             const layout_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20616                 ip,
  20617                 try ip.getOrPutString(gpa, pt.tid, "layout", .no_embedded_nulls),
  20618             ).?);
  20619             const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20620                 ip,
  20621                 try ip.getOrPutString(gpa, pt.tid, "tag_type", .no_embedded_nulls),
  20622             ).?);
  20623             const fields_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20624                 ip,
  20625                 try ip.getOrPutString(gpa, pt.tid, "fields", .no_embedded_nulls),
  20626             ).?);
  20627             const decls_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20628                 ip,
  20629                 try ip.getOrPutString(gpa, pt.tid, "decls", .no_embedded_nulls),
  20630             ).?);
  20631 
  20632             if (try decls_val.sliceLen(pt) > 0) {
  20633                 return sema.fail(block, src, "reified unions must have no decls", .{});
  20634             }
  20635             const layout = try sema.interpretBuiltinType(block, operand_src, layout_val, std.builtin.Type.ContainerLayout);
  20636 
  20637             const has_tag = tag_type_val.optionalValue(zcu) != null;
  20638 
  20639             if (has_tag) {
  20640                 switch (layout) {
  20641                     .@"extern" => return sema.fail(block, src, "extern union does not support enum tag type", .{}),
  20642                     .@"packed" => return sema.fail(block, src, "packed union does not support enum tag type", .{}),
  20643                     .auto => {},
  20644                 }
  20645             }
  20646 
  20647             const fields_arr = try sema.derefSliceAsArray(block, operand_src, fields_val, .{ .simple = .union_fields });
  20648 
  20649             return sema.reifyUnion(block, inst, src, layout, tag_type_val, fields_arr, name_strategy);
  20650         },
  20651         .@"fn" => {
  20652             const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
  20653             const calling_convention_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20654                 ip,
  20655                 try ip.getOrPutString(gpa, pt.tid, "calling_convention", .no_embedded_nulls),
  20656             ).?);
  20657             const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20658                 ip,
  20659                 try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls),
  20660             ).?);
  20661             const is_var_args_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20662                 ip,
  20663                 try ip.getOrPutString(gpa, pt.tid, "is_var_args", .no_embedded_nulls),
  20664             ).?);
  20665             const return_type_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20666                 ip,
  20667                 try ip.getOrPutString(gpa, pt.tid, "return_type", .no_embedded_nulls),
  20668             ).?);
  20669             const params_slice_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
  20670                 ip,
  20671                 try ip.getOrPutString(gpa, pt.tid, "params", .no_embedded_nulls),
  20672             ).?);
  20673 
  20674             const is_generic = is_generic_val.toBool();
  20675             if (is_generic) {
  20676                 return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{});
  20677             }
  20678 
  20679             const is_var_args = is_var_args_val.toBool();
  20680             const cc = try sema.analyzeValueAsCallconv(block, src, calling_convention_val);
  20681             if (is_var_args) {
  20682                 try sema.checkCallConvSupportsVarArgs(block, src, cc);
  20683             }
  20684 
  20685             const return_type = return_type_val.optionalValue(zcu) orelse
  20686                 return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{});
  20687 
  20688             const params_val = try sema.derefSliceAsArray(block, operand_src, params_slice_val, .{ .simple = .function_parameters });
  20689 
  20690             const args_len = try sema.usizeCast(block, src, params_val.typeOf(zcu).arrayLen(zcu));
  20691             const param_types = try sema.arena.alloc(InternPool.Index, args_len);
  20692 
  20693             var noalias_bits: u32 = 0;
  20694             for (param_types, 0..) |*param_type, i| {
  20695                 const elem_val = try params_val.elemValue(pt, i);
  20696                 const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern()));
  20697                 const param_is_generic_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex(
  20698                     ip,
  20699                     try ip.getOrPutString(gpa, pt.tid, "is_generic", .no_embedded_nulls),
  20700                 ).?);
  20701                 const param_is_noalias_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex(
  20702                     ip,
  20703                     try ip.getOrPutString(gpa, pt.tid, "is_noalias", .no_embedded_nulls),
  20704                 ).?);
  20705                 const opt_param_type_val = try elem_val.fieldValue(pt, elem_struct_type.nameIndex(
  20706                     ip,
  20707                     try ip.getOrPutString(gpa, pt.tid, "type", .no_embedded_nulls),
  20708                 ).?);
  20709 
  20710                 if (param_is_generic_val.toBool()) {
  20711                     return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{});
  20712                 }
  20713 
  20714                 const param_type_val = opt_param_type_val.optionalValue(zcu) orelse
  20715                     return sema.fail(block, src, "Type.Fn.Param.type must be non-null for @Type", .{});
  20716                 param_type.* = param_type_val.toIntern();
  20717 
  20718                 if (param_is_noalias_val.toBool()) {
  20719                     if (!Type.fromInterned(param_type.*).isPtrAtRuntime(zcu)) {
  20720                         return sema.fail(block, src, "non-pointer parameter declared noalias", .{});
  20721                     }
  20722                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, i) orelse
  20723                         return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
  20724                 }
  20725             }
  20726 
  20727             const ty = try pt.funcType(.{
  20728                 .param_types = param_types,
  20729                 .noalias_bits = noalias_bits,
  20730                 .return_type = return_type.toIntern(),
  20731                 .cc = cc,
  20732                 .is_var_args = is_var_args,
  20733             });
  20734             return Air.internedToRef(ty.toIntern());
  20735         },
  20736         .frame => return sema.failWithUseOfAsync(block, src),
  20737     }
  20738 }
  20739 
  20740 fn reifyEnum(
  20741     sema: *Sema,
  20742     block: *Block,
  20743     inst: Zir.Inst.Index,
  20744     src: LazySrcLoc,
  20745     tag_ty: Type,
  20746     is_exhaustive: bool,
  20747     fields_val: Value,
  20748     name_strategy: Zir.Inst.NameStrategy,
  20749 ) CompileError!Air.Inst.Ref {
  20750     const pt = sema.pt;
  20751     const zcu = pt.zcu;
  20752     const gpa = sema.gpa;
  20753     const ip = &zcu.intern_pool;
  20754 
  20755     // This logic must stay in sync with the structure of `std.builtin.Type.Enum` - search for `fieldValue`.
  20756 
  20757     const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu));
  20758 
  20759     // The validation work here is non-trivial, and it's possible the type already exists.
  20760     // So in this first pass, let's just construct a hash to optimize for this case. If the
  20761     // inputs turn out to be invalid, we can cancel the WIP type later.
  20762 
  20763     // For deduplication purposes, we must create a hash including all details of this type.
  20764     // TODO: use a longer hash!
  20765     var hasher = std.hash.Wyhash.init(0);
  20766     std.hash.autoHash(&hasher, tag_ty.toIntern());
  20767     std.hash.autoHash(&hasher, is_exhaustive);
  20768     std.hash.autoHash(&hasher, fields_len);
  20769 
  20770     for (0..fields_len) |field_idx| {
  20771         const field_info = try fields_val.elemValue(pt, field_idx);
  20772 
  20773         const field_name_val = try field_info.fieldValue(pt, 0);
  20774         const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1));
  20775 
  20776         const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .enum_field_name });
  20777 
  20778         std.hash.autoHash(&hasher, .{
  20779             field_name,
  20780             field_value_val.toIntern(),
  20781         });
  20782     }
  20783 
  20784     const tracked_inst = try block.trackZir(inst);
  20785 
  20786     const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{
  20787         .has_values = true,
  20788         .tag_mode = if (is_exhaustive) .explicit else .nonexhaustive,
  20789         .fields_len = fields_len,
  20790         .key = .{ .reified = .{
  20791             .zir_index = tracked_inst,
  20792             .type_hash = hasher.final(),
  20793         } },
  20794     }, false)) {
  20795         .wip => |wip| wip,
  20796         .existing => |ty| {
  20797             try sema.declareDependency(.{ .interned = ty });
  20798             try sema.addTypeReferenceEntry(src, ty);
  20799             return Air.internedToRef(ty);
  20800         },
  20801     };
  20802     var done = false;
  20803     errdefer if (!done) wip_ty.cancel(ip, pt.tid);
  20804 
  20805     if (tag_ty.zigTypeTag(zcu) != .int) {
  20806         return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
  20807     }
  20808 
  20809     const type_name = try sema.createTypeName(
  20810         block,
  20811         name_strategy,
  20812         "enum",
  20813         inst,
  20814         wip_ty.index,
  20815     );
  20816     wip_ty.setName(ip, type_name.name, type_name.nav);
  20817 
  20818     const new_namespace_index = try pt.createNamespace(.{
  20819         .parent = block.namespace.toOptional(),
  20820         .owner_type = wip_ty.index,
  20821         .file_scope = block.getFileScopeIndex(zcu),
  20822         .generation = zcu.generation,
  20823     });
  20824 
  20825     try sema.declareDependency(.{ .interned = wip_ty.index });
  20826     try sema.addTypeReferenceEntry(src, wip_ty.index);
  20827     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
  20828     wip_ty.prepare(ip, new_namespace_index);
  20829     wip_ty.setTagTy(ip, tag_ty.toIntern());
  20830     done = true;
  20831 
  20832     for (0..fields_len) |field_idx| {
  20833         const field_info = try fields_val.elemValue(pt, field_idx);
  20834 
  20835         const field_name_val = try field_info.fieldValue(pt, 0);
  20836         const field_value_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 1));
  20837 
  20838         // Don't pass a reason; first loop acts as an assertion that this is valid.
  20839         const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
  20840 
  20841         if (!try sema.intFitsInType(field_value_val, tag_ty, null)) {
  20842             // TODO: better source location
  20843             return sema.fail(block, src, "field '{f}' with enumeration value '{f}' is too large for backing int type '{f}'", .{
  20844                 field_name.fmt(ip),
  20845                 field_value_val.fmtValueSema(pt, sema),
  20846                 tag_ty.fmt(pt),
  20847             });
  20848         }
  20849 
  20850         const coerced_field_val = try pt.getCoerced(field_value_val, tag_ty);
  20851         if (wip_ty.nextField(ip, field_name, coerced_field_val.toIntern())) |conflict| {
  20852             return sema.failWithOwnedErrorMsg(block, switch (conflict.kind) {
  20853                 .name => msg: {
  20854                     const msg = try sema.errMsg(src, "duplicate enum field '{f}'", .{field_name.fmt(ip)});
  20855                     errdefer msg.destroy(gpa);
  20856                     _ = conflict.prev_field_idx; // TODO: this note is incorrect
  20857                     try sema.errNote(src, msg, "other field here", .{});
  20858                     break :msg msg;
  20859                 },
  20860                 .value => msg: {
  20861                     const msg = try sema.errMsg(src, "enum tag value {f} already taken", .{field_value_val.fmtValueSema(pt, sema)});
  20862                     errdefer msg.destroy(gpa);
  20863                     _ = conflict.prev_field_idx; // TODO: this note is incorrect
  20864                     try sema.errNote(src, msg, "other enum tag value here", .{});
  20865                     break :msg msg;
  20866                 },
  20867             });
  20868         }
  20869     }
  20870 
  20871     if (!is_exhaustive and fields_len > 1 and std.math.log2_int(u64, fields_len) == tag_ty.bitSize(zcu)) {
  20872         return sema.fail(block, src, "non-exhaustive enum specified every value", .{});
  20873     }
  20874 
  20875     codegen_type: {
  20876         if (zcu.comp.config.use_llvm) break :codegen_type;
  20877         if (block.ownerModule().strip) break :codegen_type;
  20878         // This job depends on any resolve_type_fully jobs queued up before it.
  20879         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
  20880         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
  20881     }
  20882     return Air.internedToRef(wip_ty.index);
  20883 }
  20884 
  20885 fn reifyUnion(
  20886     sema: *Sema,
  20887     block: *Block,
  20888     inst: Zir.Inst.Index,
  20889     src: LazySrcLoc,
  20890     layout: std.builtin.Type.ContainerLayout,
  20891     opt_tag_type_val: Value,
  20892     fields_val: Value,
  20893     name_strategy: Zir.Inst.NameStrategy,
  20894 ) CompileError!Air.Inst.Ref {
  20895     const pt = sema.pt;
  20896     const zcu = pt.zcu;
  20897     const gpa = sema.gpa;
  20898     const ip = &zcu.intern_pool;
  20899 
  20900     // This logic must stay in sync with the structure of `std.builtin.Type.Union` - search for `fieldValue`.
  20901 
  20902     const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu));
  20903 
  20904     // The validation work here is non-trivial, and it's possible the type already exists.
  20905     // So in this first pass, let's just construct a hash to optimize for this case. If the
  20906     // inputs turn out to be invalid, we can cancel the WIP type later.
  20907 
  20908     // For deduplication purposes, we must create a hash including all details of this type.
  20909     // TODO: use a longer hash!
  20910     var hasher = std.hash.Wyhash.init(0);
  20911     std.hash.autoHash(&hasher, layout);
  20912     std.hash.autoHash(&hasher, opt_tag_type_val.toIntern());
  20913     std.hash.autoHash(&hasher, fields_len);
  20914 
  20915     for (0..fields_len) |field_idx| {
  20916         const field_info = try fields_val.elemValue(pt, field_idx);
  20917 
  20918         const field_name_val = try field_info.fieldValue(pt, 0);
  20919         const field_type_val = try field_info.fieldValue(pt, 1);
  20920         const field_align_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 2));
  20921 
  20922         const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .union_field_name });
  20923         std.hash.autoHash(&hasher, .{
  20924             field_name,
  20925             field_type_val.toIntern(),
  20926             field_align_val.toIntern(),
  20927         });
  20928     }
  20929 
  20930     const tracked_inst = try block.trackZir(inst);
  20931 
  20932     const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, .{
  20933         .flags = .{
  20934             .layout = layout,
  20935             .status = .none,
  20936             .runtime_tag = if (opt_tag_type_val.optionalValue(zcu) != null)
  20937                 .tagged
  20938             else if (layout != .auto)
  20939                 .none
  20940             else switch (block.wantSafeTypes()) {
  20941                 true => .safety,
  20942                 false => .none,
  20943             },
  20944             .any_aligned_fields = layout != .@"packed",
  20945             .requires_comptime = .unknown,
  20946             .assumed_runtime_bits = false,
  20947             .assumed_pointer_aligned = false,
  20948             .alignment = .none,
  20949         },
  20950         .fields_len = fields_len,
  20951         .enum_tag_ty = .none, // set later because not yet validated
  20952         .field_types = &.{}, // set later
  20953         .field_aligns = &.{}, // set later
  20954         .key = .{ .reified = .{
  20955             .zir_index = tracked_inst,
  20956             .type_hash = hasher.final(),
  20957         } },
  20958     }, false)) {
  20959         .wip => |wip| wip,
  20960         .existing => |ty| {
  20961             try sema.declareDependency(.{ .interned = ty });
  20962             try sema.addTypeReferenceEntry(src, ty);
  20963             return Air.internedToRef(ty);
  20964         },
  20965     };
  20966     errdefer wip_ty.cancel(ip, pt.tid);
  20967 
  20968     const type_name = try sema.createTypeName(
  20969         block,
  20970         name_strategy,
  20971         "union",
  20972         inst,
  20973         wip_ty.index,
  20974     );
  20975     wip_ty.setName(ip, type_name.name, type_name.nav);
  20976 
  20977     const loaded_union = ip.loadUnionType(wip_ty.index);
  20978 
  20979     const enum_tag_ty, const has_explicit_tag = if (opt_tag_type_val.optionalValue(zcu)) |tag_type_val| tag_ty: {
  20980         switch (ip.indexToKey(tag_type_val.toIntern())) {
  20981             .enum_type => {},
  20982             else => return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{}),
  20983         }
  20984         const enum_tag_ty = tag_type_val.toType();
  20985 
  20986         // We simply track which fields of the tag type have been seen.
  20987         const tag_ty_fields_len = enum_tag_ty.enumFieldCount(zcu);
  20988         var seen_tags = try std.DynamicBitSetUnmanaged.initEmpty(sema.arena, tag_ty_fields_len);
  20989 
  20990         for (0..fields_len) |field_idx| {
  20991             const field_info = try fields_val.elemValue(pt, field_idx);
  20992 
  20993             const field_name_val = try field_info.fieldValue(pt, 0);
  20994             const field_type_val = try field_info.fieldValue(pt, 1);
  20995             const field_alignment_val = try field_info.fieldValue(pt, 2);
  20996 
  20997             // Don't pass a reason; first loop acts as an assertion that this is valid.
  20998             const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
  20999 
  21000             const enum_index = enum_tag_ty.enumFieldIndex(field_name, zcu) orelse {
  21001                 // TODO: better source location
  21002                 return sema.fail(block, src, "no field named '{f}' in enum '{f}'", .{
  21003                     field_name.fmt(ip), enum_tag_ty.fmt(pt),
  21004                 });
  21005             };
  21006             if (seen_tags.isSet(enum_index)) {
  21007                 // TODO: better source location
  21008                 return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)});
  21009             }
  21010             seen_tags.set(enum_index);
  21011 
  21012             loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern();
  21013             const byte_align = try field_alignment_val.toUnsignedIntSema(pt);
  21014             if (layout == .@"packed") {
  21015                 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{});
  21016             } else {
  21017                 loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align);
  21018             }
  21019         }
  21020 
  21021         if (tag_ty_fields_len > fields_len) return sema.failWithOwnedErrorMsg(block, msg: {
  21022             const msg = try sema.errMsg(src, "enum fields missing in union", .{});
  21023             errdefer msg.destroy(gpa);
  21024             var it = seen_tags.iterator(.{ .kind = .unset });
  21025             while (it.next()) |enum_index| {
  21026                 const field_name = enum_tag_ty.enumFieldName(enum_index, zcu);
  21027                 try sema.addFieldErrNote(enum_tag_ty, enum_index, msg, "field '{f}' missing, declared here", .{
  21028                     field_name.fmt(ip),
  21029                 });
  21030             }
  21031             try sema.addDeclaredHereNote(msg, enum_tag_ty);
  21032             break :msg msg;
  21033         });
  21034 
  21035         break :tag_ty .{ enum_tag_ty.toIntern(), true };
  21036     } else tag_ty: {
  21037         // We must track field names and set up the tag type ourselves.
  21038         var field_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
  21039         try field_names.ensureTotalCapacity(sema.arena, fields_len);
  21040 
  21041         for (0..fields_len) |field_idx| {
  21042             const field_info = try fields_val.elemValue(pt, field_idx);
  21043 
  21044             const field_name_val = try field_info.fieldValue(pt, 0);
  21045             const field_type_val = try field_info.fieldValue(pt, 1);
  21046             const field_alignment_val = try field_info.fieldValue(pt, 2);
  21047 
  21048             // Don't pass a reason; first loop acts as an assertion that this is valid.
  21049             const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
  21050             const gop = field_names.getOrPutAssumeCapacity(field_name);
  21051             if (gop.found_existing) {
  21052                 // TODO: better source location
  21053                 return sema.fail(block, src, "duplicate union field {f}", .{field_name.fmt(ip)});
  21054             }
  21055 
  21056             loaded_union.field_types.get(ip)[field_idx] = field_type_val.toIntern();
  21057             const byte_align = try field_alignment_val.toUnsignedIntSema(pt);
  21058             if (layout == .@"packed") {
  21059                 if (byte_align != 0) return sema.fail(block, src, "alignment of a packed union field must be set to 0", .{});
  21060             } else {
  21061                 loaded_union.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align);
  21062             }
  21063         }
  21064 
  21065         const enum_tag_ty = try sema.generateUnionTagTypeSimple(block, field_names.keys(), wip_ty.index, type_name.name);
  21066         break :tag_ty .{ enum_tag_ty, false };
  21067     };
  21068     errdefer if (!has_explicit_tag) ip.remove(pt.tid, enum_tag_ty); // remove generated tag type on error
  21069 
  21070     for (loaded_union.field_types.get(ip)) |field_ty_ip| {
  21071         const field_ty: Type = .fromInterned(field_ty_ip);
  21072         if (field_ty.zigTypeTag(zcu) == .@"opaque") {
  21073             return sema.failWithOwnedErrorMsg(block, msg: {
  21074                 const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  21075                 errdefer msg.destroy(gpa);
  21076 
  21077                 try sema.addDeclaredHereNote(msg, field_ty);
  21078                 break :msg msg;
  21079             });
  21080         }
  21081         if (layout == .@"extern" and !try sema.validateExternType(field_ty, .union_field)) {
  21082             return sema.failWithOwnedErrorMsg(block, msg: {
  21083                 const msg = try sema.errMsg(src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  21084                 errdefer msg.destroy(gpa);
  21085 
  21086                 try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .union_field);
  21087 
  21088                 try sema.addDeclaredHereNote(msg, field_ty);
  21089                 break :msg msg;
  21090             });
  21091         } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) {
  21092             return sema.failWithOwnedErrorMsg(block, msg: {
  21093                 const msg = try sema.errMsg(src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  21094                 errdefer msg.destroy(gpa);
  21095 
  21096                 try sema.explainWhyTypeIsNotPacked(msg, src, field_ty);
  21097 
  21098                 try sema.addDeclaredHereNote(msg, field_ty);
  21099                 break :msg msg;
  21100             });
  21101         }
  21102     }
  21103 
  21104     loaded_union.setTagType(ip, enum_tag_ty);
  21105     loaded_union.setStatus(ip, .have_field_types);
  21106 
  21107     const new_namespace_index = try pt.createNamespace(.{
  21108         .parent = block.namespace.toOptional(),
  21109         .owner_type = wip_ty.index,
  21110         .file_scope = block.getFileScopeIndex(zcu),
  21111         .generation = zcu.generation,
  21112     });
  21113 
  21114     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
  21115     codegen_type: {
  21116         if (zcu.comp.config.use_llvm) break :codegen_type;
  21117         if (block.ownerModule().strip) break :codegen_type;
  21118         // This job depends on any resolve_type_fully jobs queued up before it.
  21119         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
  21120         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
  21121     }
  21122     try sema.declareDependency(.{ .interned = wip_ty.index });
  21123     try sema.addTypeReferenceEntry(src, wip_ty.index);
  21124     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
  21125     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
  21126 }
  21127 
  21128 fn reifyTuple(
  21129     sema: *Sema,
  21130     block: *Block,
  21131     src: LazySrcLoc,
  21132     fields_val: Value,
  21133 ) CompileError!Air.Inst.Ref {
  21134     const pt = sema.pt;
  21135     const zcu = pt.zcu;
  21136     const gpa = sema.gpa;
  21137     const ip = &zcu.intern_pool;
  21138 
  21139     const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu));
  21140 
  21141     const types = try sema.arena.alloc(InternPool.Index, fields_len);
  21142     const inits = try sema.arena.alloc(InternPool.Index, fields_len);
  21143 
  21144     for (types, inits, 0..) |*field_ty, *field_init, field_idx| {
  21145         const field_info = try fields_val.elemValue(pt, field_idx);
  21146 
  21147         const field_name_val = try field_info.fieldValue(pt, 0);
  21148         const field_type_val = try field_info.fieldValue(pt, 1);
  21149         const field_default_value_val = try field_info.fieldValue(pt, 2);
  21150         const field_is_comptime_val = try field_info.fieldValue(pt, 3);
  21151         const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4));
  21152 
  21153         const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .tuple_field_name });
  21154         const field_type = field_type_val.toType();
  21155         const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: {
  21156             const ptr_ty = try pt.singleConstPtrType(field_type_val.toType());
  21157             // We need to do this deref here, so we won't check for this error case later on.
  21158             const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime(
  21159                 block,
  21160                 src,
  21161                 .{ .simple = .tuple_field_default_value },
  21162             );
  21163             // Resolve the value so that lazy values do not create distinct types.
  21164             break :d (try sema.resolveLazyValue(val)).toIntern();
  21165         } else .none;
  21166 
  21167         const field_name_index = field_name.toUnsigned(ip) orelse return sema.fail(
  21168             block,
  21169             src,
  21170             "tuple cannot have non-numeric field '{f}'",
  21171             .{field_name.fmt(ip)},
  21172         );
  21173         if (field_name_index != field_idx) {
  21174             return sema.fail(
  21175                 block,
  21176                 src,
  21177                 "tuple field name '{d}' does not match field index {d}",
  21178                 .{ field_name_index, field_idx },
  21179             );
  21180         }
  21181 
  21182         try sema.validateTupleFieldType(block, field_type, src);
  21183 
  21184         {
  21185             const alignment_ok = ok: {
  21186                 if (field_alignment_val.toIntern() == .zero) break :ok true;
  21187                 const given_align = try field_alignment_val.getUnsignedIntSema(pt) orelse break :ok false;
  21188                 const abi_align = (try field_type.abiAlignmentSema(pt)).toByteUnits() orelse 0;
  21189                 break :ok abi_align == given_align;
  21190             };
  21191             if (!alignment_ok) {
  21192                 return sema.fail(block, src, "tuple fields cannot specify alignment", .{});
  21193             }
  21194         }
  21195 
  21196         if (field_is_comptime_val.toBool() and field_default_value == .none) {
  21197             return sema.fail(block, src, "comptime field without default initialization value", .{});
  21198         }
  21199 
  21200         if (!field_is_comptime_val.toBool() and field_default_value != .none) {
  21201             return sema.fail(block, src, "non-comptime tuple fields cannot specify default initialization value", .{});
  21202         }
  21203 
  21204         const default_or_opv: InternPool.Index = default: {
  21205             if (field_default_value != .none) {
  21206                 break :default field_default_value;
  21207             }
  21208             if (try sema.typeHasOnePossibleValue(field_type)) |opv| {
  21209                 break :default opv.toIntern();
  21210             }
  21211             break :default .none;
  21212         };
  21213 
  21214         field_ty.* = field_type.toIntern();
  21215         field_init.* = default_or_opv;
  21216     }
  21217 
  21218     return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, pt.tid, .{
  21219         .types = types,
  21220         .values = inits,
  21221     }));
  21222 }
  21223 
  21224 fn reifyStruct(
  21225     sema: *Sema,
  21226     block: *Block,
  21227     inst: Zir.Inst.Index,
  21228     src: LazySrcLoc,
  21229     layout: std.builtin.Type.ContainerLayout,
  21230     opt_backing_int_val: Value,
  21231     fields_val: Value,
  21232     name_strategy: Zir.Inst.NameStrategy,
  21233 ) CompileError!Air.Inst.Ref {
  21234     const pt = sema.pt;
  21235     const zcu = pt.zcu;
  21236     const gpa = sema.gpa;
  21237     const ip = &zcu.intern_pool;
  21238 
  21239     // This logic must stay in sync with the structure of `std.builtin.Type.Struct` - search for `fieldValue`.
  21240 
  21241     const fields_len: u32 = @intCast(fields_val.typeOf(zcu).arrayLen(zcu));
  21242 
  21243     // The validation work here is non-trivial, and it's possible the type already exists.
  21244     // So in this first pass, let's just construct a hash to optimize for this case. If the
  21245     // inputs turn out to be invalid, we can cancel the WIP type later.
  21246 
  21247     // For deduplication purposes, we must create a hash including all details of this type.
  21248     // TODO: use a longer hash!
  21249     var hasher = std.hash.Wyhash.init(0);
  21250     std.hash.autoHash(&hasher, layout);
  21251     std.hash.autoHash(&hasher, opt_backing_int_val.toIntern());
  21252     std.hash.autoHash(&hasher, fields_len);
  21253 
  21254     var any_comptime_fields = false;
  21255     var any_default_inits = false;
  21256 
  21257     for (0..fields_len) |field_idx| {
  21258         const field_info = try fields_val.elemValue(pt, field_idx);
  21259 
  21260         const field_name_val = try field_info.fieldValue(pt, 0);
  21261         const field_type_val = try field_info.fieldValue(pt, 1);
  21262         const field_default_value_val = try field_info.fieldValue(pt, 2);
  21263         const field_is_comptime_val = try field_info.fieldValue(pt, 3);
  21264         const field_alignment_val = try sema.resolveLazyValue(try field_info.fieldValue(pt, 4));
  21265 
  21266         const field_name = try sema.sliceToIpString(block, src, field_name_val, .{ .simple = .struct_field_name });
  21267         const field_is_comptime = field_is_comptime_val.toBool();
  21268         const field_default_value: InternPool.Index = if (field_default_value_val.optionalValue(zcu)) |ptr_val| d: {
  21269             const ptr_ty = try pt.singleConstPtrType(field_type_val.toType());
  21270             // We need to do this deref here, so we won't check for this error case later on.
  21271             const val = try sema.pointerDeref(block, src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime(
  21272                 block,
  21273                 src,
  21274                 .{ .simple = .struct_field_default_value },
  21275             );
  21276             // Resolve the value so that lazy values do not create distinct types.
  21277             break :d (try sema.resolveLazyValue(val)).toIntern();
  21278         } else .none;
  21279 
  21280         std.hash.autoHash(&hasher, .{
  21281             field_name,
  21282             field_type_val.toIntern(),
  21283             field_default_value,
  21284             field_is_comptime,
  21285             field_alignment_val.toIntern(),
  21286         });
  21287 
  21288         if (field_is_comptime) any_comptime_fields = true;
  21289         if (field_default_value != .none) any_default_inits = true;
  21290     }
  21291 
  21292     const tracked_inst = try block.trackZir(inst);
  21293 
  21294     const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
  21295         .layout = layout,
  21296         .fields_len = fields_len,
  21297         .known_non_opv = false,
  21298         .requires_comptime = .unknown,
  21299         .any_comptime_fields = any_comptime_fields,
  21300         .any_default_inits = any_default_inits,
  21301         .any_aligned_fields = layout != .@"packed",
  21302         .inits_resolved = true,
  21303         .key = .{ .reified = .{
  21304             .zir_index = tracked_inst,
  21305             .type_hash = hasher.final(),
  21306         } },
  21307     }, false)) {
  21308         .wip => |wip| wip,
  21309         .existing => |ty| {
  21310             try sema.declareDependency(.{ .interned = ty });
  21311             try sema.addTypeReferenceEntry(src, ty);
  21312             return Air.internedToRef(ty);
  21313         },
  21314     };
  21315     errdefer wip_ty.cancel(ip, pt.tid);
  21316 
  21317     const type_name = try sema.createTypeName(
  21318         block,
  21319         name_strategy,
  21320         "struct",
  21321         inst,
  21322         wip_ty.index,
  21323     );
  21324     wip_ty.setName(ip, type_name.name, type_name.nav);
  21325 
  21326     const struct_type = ip.loadStructType(wip_ty.index);
  21327 
  21328     for (0..fields_len) |field_idx| {
  21329         const field_info = try fields_val.elemValue(pt, field_idx);
  21330 
  21331         const field_name_val = try field_info.fieldValue(pt, 0);
  21332         const field_type_val = try field_info.fieldValue(pt, 1);
  21333         const field_default_value_val = try field_info.fieldValue(pt, 2);
  21334         const field_is_comptime_val = try field_info.fieldValue(pt, 3);
  21335         const field_alignment_val = try field_info.fieldValue(pt, 4);
  21336 
  21337         const field_ty = field_type_val.toType();
  21338         // Don't pass a reason; first loop acts as an assertion that this is valid.
  21339         const field_name = try sema.sliceToIpString(block, src, field_name_val, undefined);
  21340         if (struct_type.addFieldName(ip, field_name)) |prev_index| {
  21341             _ = prev_index; // TODO: better source location
  21342             return sema.fail(block, src, "duplicate struct field name {f}", .{field_name.fmt(ip)});
  21343         }
  21344 
  21345         if (!try sema.intFitsInType(field_alignment_val, align_ty, null)) {
  21346             return sema.fail(block, src, "alignment must fit in '{f}'", .{align_ty.fmt(pt)});
  21347         }
  21348         const byte_align = try field_alignment_val.toUnsignedIntSema(pt);
  21349         if (layout == .@"packed") {
  21350             if (byte_align != 0) return sema.fail(block, src, "alignment of a packed struct field must be set to 0", .{});
  21351         } else {
  21352             struct_type.field_aligns.get(ip)[field_idx] = try sema.validateAlign(block, src, byte_align);
  21353         }
  21354 
  21355         const field_is_comptime = field_is_comptime_val.toBool();
  21356         if (field_is_comptime) {
  21357             assert(any_comptime_fields);
  21358             switch (layout) {
  21359                 .@"extern" => return sema.fail(block, src, "extern struct fields cannot be marked comptime", .{}),
  21360                 .@"packed" => return sema.fail(block, src, "packed struct fields cannot be marked comptime", .{}),
  21361                 .auto => struct_type.setFieldComptime(ip, field_idx),
  21362             }
  21363         }
  21364 
  21365         const field_default: InternPool.Index = d: {
  21366             if (!any_default_inits) break :d .none;
  21367             const ptr_val = field_default_value_val.optionalValue(zcu) orelse break :d .none;
  21368             const ptr_ty = try pt.singleConstPtrType(field_ty);
  21369             // Asserted comptime-dereferencable above.
  21370             const val = (try sema.pointerDeref(block, src, ptr_val, ptr_ty)).?;
  21371             // We already resolved this for deduplication, so we may as well do it now.
  21372             break :d (try sema.resolveLazyValue(val)).toIntern();
  21373         };
  21374 
  21375         if (field_is_comptime and field_default == .none) {
  21376             return sema.fail(block, src, "comptime field without default initialization value", .{});
  21377         }
  21378 
  21379         struct_type.field_types.get(ip)[field_idx] = field_type_val.toIntern();
  21380         if (field_default != .none) {
  21381             struct_type.field_inits.get(ip)[field_idx] = field_default;
  21382         }
  21383 
  21384         if (field_ty.zigTypeTag(zcu) == .@"opaque") {
  21385             return sema.failWithOwnedErrorMsg(block, msg: {
  21386                 const msg = try sema.errMsg(src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  21387                 errdefer msg.destroy(gpa);
  21388 
  21389                 try sema.addDeclaredHereNote(msg, field_ty);
  21390                 break :msg msg;
  21391             });
  21392         }
  21393         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  21394             return sema.failWithOwnedErrorMsg(block, msg: {
  21395                 const msg = try sema.errMsg(src, "struct fields cannot be 'noreturn'", .{});
  21396                 errdefer msg.destroy(gpa);
  21397 
  21398                 try sema.addDeclaredHereNote(msg, field_ty);
  21399                 break :msg msg;
  21400             });
  21401         }
  21402         if (layout == .@"extern" and !try sema.validateExternType(field_ty, .struct_field)) {
  21403             return sema.failWithOwnedErrorMsg(block, msg: {
  21404                 const msg = try sema.errMsg(src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  21405                 errdefer msg.destroy(gpa);
  21406 
  21407                 try sema.explainWhyTypeIsNotExtern(msg, src, field_ty, .struct_field);
  21408 
  21409                 try sema.addDeclaredHereNote(msg, field_ty);
  21410                 break :msg msg;
  21411             });
  21412         } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) {
  21413             return sema.failWithOwnedErrorMsg(block, msg: {
  21414                 const msg = try sema.errMsg(src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  21415                 errdefer msg.destroy(gpa);
  21416 
  21417                 try sema.explainWhyTypeIsNotPacked(msg, src, field_ty);
  21418 
  21419                 try sema.addDeclaredHereNote(msg, field_ty);
  21420                 break :msg msg;
  21421             });
  21422         }
  21423     }
  21424 
  21425     if (layout == .@"packed") {
  21426         var fields_bit_sum: u64 = 0;
  21427         for (0..struct_type.field_types.len) |field_idx| {
  21428             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_idx]);
  21429             field_ty.resolveLayout(pt) catch |err| switch (err) {
  21430                 error.AnalysisFail => {
  21431                     const msg = sema.err orelse return err;
  21432                     try sema.errNote(src, msg, "while checking a field of this struct", .{});
  21433                     return err;
  21434                 },
  21435                 else => return err,
  21436             };
  21437             fields_bit_sum += field_ty.bitSize(zcu);
  21438         }
  21439 
  21440         if (opt_backing_int_val.optionalValue(zcu)) |backing_int_val| {
  21441             const backing_int_ty = backing_int_val.toType();
  21442             try sema.checkBackingIntType(block, src, backing_int_ty, fields_bit_sum);
  21443             struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
  21444         } else {
  21445             const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum));
  21446             struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
  21447         }
  21448     }
  21449 
  21450     const new_namespace_index = try pt.createNamespace(.{
  21451         .parent = block.namespace.toOptional(),
  21452         .owner_type = wip_ty.index,
  21453         .file_scope = block.getFileScopeIndex(zcu),
  21454         .generation = zcu.generation,
  21455     });
  21456 
  21457     try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
  21458     codegen_type: {
  21459         if (zcu.comp.config.use_llvm) break :codegen_type;
  21460         if (block.ownerModule().strip) break :codegen_type;
  21461         // This job depends on any resolve_type_fully jobs queued up before it.
  21462         zcu.comp.link_prog_node.increaseEstimatedTotalItems(1);
  21463         try zcu.comp.queueJob(.{ .link_type = wip_ty.index });
  21464     }
  21465     try sema.declareDependency(.{ .interned = wip_ty.index });
  21466     try sema.addTypeReferenceEntry(src, wip_ty.index);
  21467     if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index);
  21468     return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
  21469 }
  21470 
  21471 fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
  21472     const pt = sema.pt;
  21473     const va_list_ty = try sema.getBuiltinType(src, .VaList);
  21474     const va_list_ptr = try pt.singleMutPtrType(va_list_ty);
  21475 
  21476     const inst = try sema.resolveInst(zir_ref);
  21477     return sema.coerce(block, va_list_ptr, inst, src);
  21478 }
  21479 
  21480 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21481     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  21482     const src = block.nodeOffset(extra.node);
  21483     const va_list_src = block.builtinCallArgSrc(extra.node, 0);
  21484     const ty_src = block.builtinCallArgSrc(extra.node, 1);
  21485 
  21486     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);
  21487     const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);
  21488 
  21489     if (!try sema.validateExternType(arg_ty, .param_ty)) {
  21490         const msg = msg: {
  21491             const msg = try sema.errMsg(ty_src, "cannot get '{f}' from variadic argument", .{arg_ty.fmt(sema.pt)});
  21492             errdefer msg.destroy(sema.gpa);
  21493 
  21494             try sema.explainWhyTypeIsNotExtern(msg, ty_src, arg_ty, .param_ty);
  21495 
  21496             try sema.addDeclaredHereNote(msg, arg_ty);
  21497             break :msg msg;
  21498         };
  21499         return sema.failWithOwnedErrorMsg(block, msg);
  21500     }
  21501 
  21502     try sema.requireRuntimeBlock(block, src, null);
  21503     return block.addTyOp(.c_va_arg, arg_ty, va_list_ref);
  21504 }
  21505 
  21506 fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21507     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  21508     const src = block.nodeOffset(extra.node);
  21509     const va_list_src = block.builtinCallArgSrc(extra.node, 0);
  21510 
  21511     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  21512     const va_list_ty = try sema.getBuiltinType(src, .VaList);
  21513 
  21514     try sema.requireRuntimeBlock(block, src, null);
  21515     return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref);
  21516 }
  21517 
  21518 fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21519     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  21520     const src = block.nodeOffset(extra.node);
  21521     const va_list_src = block.builtinCallArgSrc(extra.node, 0);
  21522 
  21523     const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
  21524 
  21525     try sema.requireRuntimeBlock(block, src, null);
  21526     return block.addUnOp(.c_va_end, va_list_ref);
  21527 }
  21528 
  21529 fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21530     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  21531     const src = block.nodeOffset(src_node);
  21532 
  21533     const va_list_ty = try sema.getBuiltinType(src, .VaList);
  21534     try sema.requireRuntimeBlock(block, src, null);
  21535     return block.addInst(.{
  21536         .tag = .c_va_start,
  21537         .data = .{ .ty = va_list_ty },
  21538     });
  21539 }
  21540 
  21541 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21542     const pt = sema.pt;
  21543     const zcu = pt.zcu;
  21544     const ip = &zcu.intern_pool;
  21545 
  21546     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  21547     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  21548     const ty = try sema.resolveType(block, ty_src, inst_data.operand);
  21549 
  21550     const type_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{f}", .{ty.fmt(pt)}, .no_embedded_nulls);
  21551     return sema.addNullTerminatedStrLit(type_name);
  21552 }
  21553 
  21554 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21555     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  21556     const src = block.nodeOffset(inst_data.src_node);
  21557     return sema.failWithUseOfAsync(block, src);
  21558 }
  21559 
  21560 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21561     const pt = sema.pt;
  21562     const zcu = pt.zcu;
  21563     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21564     const src = block.nodeOffset(inst_data.src_node);
  21565     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21566     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  21567     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat");
  21568     const operand = try sema.resolveInst(extra.rhs);
  21569     const operand_ty = sema.typeOf(operand);
  21570 
  21571     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
  21572     const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
  21573 
  21574     const dest_scalar_ty = dest_ty.scalarType(zcu);
  21575     const operand_scalar_ty = operand_ty.scalarType(zcu);
  21576 
  21577     _ = try sema.checkIntType(block, src, dest_scalar_ty);
  21578     try sema.checkFloatType(block, operand_src, operand_scalar_ty);
  21579 
  21580     if (try sema.resolveValue(operand)) |operand_val| {
  21581         const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate);
  21582         return Air.internedToRef(result_val.toIntern());
  21583     } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
  21584         return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int });
  21585     }
  21586 
  21587     try sema.requireRuntimeBlock(block, src, operand_src);
  21588     if (dest_scalar_ty.intInfo(zcu).bits == 0) {
  21589         if (block.wantSafety()) {
  21590             // Emit an explicit safety check. We can do this one like `abs(x) < 1`.
  21591             const abs_ref = try block.addTyOp(.abs, operand_ty, operand);
  21592             const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref;
  21593             const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern());
  21594             const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref);
  21595             try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds);
  21596         }
  21597         const scalar_val = try pt.intValue(dest_scalar_ty, 0);
  21598         if (!is_vector) return Air.internedToRef(scalar_val.toIntern());
  21599         return Air.internedToRef(try pt.intern(.{ .aggregate = .{
  21600             .ty = dest_ty.toIntern(),
  21601             .storage = .{ .repeated_elem = scalar_val.toIntern() },
  21602         } }));
  21603     }
  21604     if (block.wantSafety()) {
  21605         try sema.preparePanicId(src, .integer_part_out_of_bounds);
  21606         return block.addTyOp(switch (block.float_mode) {
  21607             .optimized => .int_from_float_optimized_safe,
  21608             .strict => .int_from_float_safe,
  21609         }, dest_ty, operand);
  21610     }
  21611     return block.addTyOp(switch (block.float_mode) {
  21612         .optimized => .int_from_float_optimized,
  21613         .strict => .int_from_float,
  21614     }, dest_ty, operand);
  21615 }
  21616 
  21617 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21618     const pt = sema.pt;
  21619     const zcu = pt.zcu;
  21620     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21621     const src = block.nodeOffset(inst_data.src_node);
  21622     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21623     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  21624     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt");
  21625     const operand = try sema.resolveInst(extra.rhs);
  21626     const operand_ty = sema.typeOf(operand);
  21627 
  21628     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
  21629 
  21630     const dest_scalar_ty = dest_ty.scalarType(zcu);
  21631     const operand_scalar_ty = operand_ty.scalarType(zcu);
  21632 
  21633     try sema.checkFloatType(block, src, dest_scalar_ty);
  21634     _ = try sema.checkIntType(block, operand_src, operand_scalar_ty);
  21635 
  21636     if (try sema.resolveValue(operand)) |operand_val| {
  21637         const result_val = try operand_val.floatFromIntAdvanced(sema.arena, operand_ty, dest_ty, pt, .sema);
  21638         return Air.internedToRef(result_val.toIntern());
  21639     } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) {
  21640         return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_float });
  21641     }
  21642 
  21643     try sema.requireRuntimeBlock(block, src, operand_src);
  21644     return block.addTyOp(.float_from_int, dest_ty, operand);
  21645 }
  21646 
  21647 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21648     const pt = sema.pt;
  21649     const zcu = pt.zcu;
  21650     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21651     const src = block.nodeOffset(inst_data.src_node);
  21652 
  21653     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21654 
  21655     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  21656     const operand_res = try sema.resolveInst(extra.rhs);
  21657 
  21658     const uncoerced_operand_ty = sema.typeOf(operand_res);
  21659     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt");
  21660     try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, uncoerced_operand_ty, src, operand_src);
  21661 
  21662     const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
  21663     const operand_ty: Type = if (is_vector) operand_ty: {
  21664         const len = dest_ty.vectorLen(zcu);
  21665         break :operand_ty try pt.vectorType(.{ .child = .usize_type, .len = len });
  21666     } else .usize;
  21667 
  21668     const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src);
  21669 
  21670     const ptr_ty = dest_ty.scalarType(zcu);
  21671     try sema.checkPtrType(block, src, ptr_ty, true);
  21672 
  21673     const elem_ty = ptr_ty.elemType2(zcu);
  21674     const ptr_align = try ptr_ty.ptrAlignmentSema(pt);
  21675 
  21676     if (ptr_ty.isSlice(zcu)) {
  21677         const msg = msg: {
  21678             const msg = try sema.errMsg(src, "integer cannot be converted to slice type '{f}'", .{ptr_ty.fmt(pt)});
  21679             errdefer msg.destroy(sema.gpa);
  21680             try sema.errNote(src, msg, "slice length cannot be inferred from address", .{});
  21681             break :msg msg;
  21682         };
  21683         return sema.failWithOwnedErrorMsg(block, msg);
  21684     }
  21685 
  21686     if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
  21687         if (!is_vector) {
  21688             const ptr_val = try sema.ptrFromIntVal(block, operand_src, val, ptr_ty, ptr_align);
  21689             return Air.internedToRef(ptr_val.toIntern());
  21690         }
  21691         const len = dest_ty.vectorLen(zcu);
  21692         const new_elems = try sema.arena.alloc(InternPool.Index, len);
  21693         for (new_elems, 0..) |*new_elem, i| {
  21694             const elem = try val.elemValue(pt, i);
  21695             const ptr_val = try sema.ptrFromIntVal(block, operand_src, elem, ptr_ty, ptr_align);
  21696             new_elem.* = ptr_val.toIntern();
  21697         }
  21698         return Air.internedToRef(try pt.intern(.{ .aggregate = .{
  21699             .ty = dest_ty.toIntern(),
  21700             .storage = .{ .elems = new_elems },
  21701         } }));
  21702     }
  21703     if (try ptr_ty.comptimeOnlySema(pt)) {
  21704         return sema.failWithOwnedErrorMsg(block, msg: {
  21705             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)});
  21706             errdefer msg.destroy(sema.gpa);
  21707 
  21708             try sema.explainWhyTypeIsComptime(msg, src, ptr_ty);
  21709             break :msg msg;
  21710         });
  21711     }
  21712     try sema.requireRuntimeBlock(block, src, operand_src);
  21713     try sema.checkLogicalPtrOperation(block, src, ptr_ty);
  21714     if (block.wantSafety() and (try elem_ty.hasRuntimeBitsSema(pt) or elem_ty.zigTypeTag(zcu) == .@"fn")) {
  21715         if (!ptr_ty.isAllowzeroPtr(zcu)) {
  21716             const is_non_zero = if (is_vector) all_non_zero: {
  21717                 const zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern());
  21718                 const is_non_zero = try block.addCmpVector(operand_coerced, zero_usize, .neq);
  21719                 break :all_non_zero try block.addReduce(is_non_zero, .And);
  21720             } else try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
  21721             try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null);
  21722         }
  21723         if (ptr_align.compare(.gt, .@"1")) {
  21724             const align_bytes_minus_1 = ptr_align.toByteUnits().? - 1;
  21725             const align_mask = Air.internedToRef((try sema.splat(operand_ty, try pt.intValue(
  21726                 .usize,
  21727                 if (elem_ty.fnPtrMaskOrNull(zcu)) |mask|
  21728                     align_bytes_minus_1 & mask
  21729                 else
  21730                     align_bytes_minus_1,
  21731             ))).toIntern());
  21732             const remainder = try block.addBinOp(.bit_and, operand_coerced, align_mask);
  21733             const is_aligned = if (is_vector) all_aligned: {
  21734                 const splat_zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern());
  21735                 const is_aligned = try block.addCmpVector(remainder, splat_zero_usize, .eq);
  21736                 break :all_aligned try block.addReduce(is_aligned, .And);
  21737             } else try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  21738             try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment);
  21739         }
  21740     }
  21741     return block.addBitCast(dest_ty, operand_coerced);
  21742 }
  21743 
  21744 fn ptrFromIntVal(
  21745     sema: *Sema,
  21746     block: *Block,
  21747     operand_src: LazySrcLoc,
  21748     operand_val: Value,
  21749     ptr_ty: Type,
  21750     ptr_align: Alignment,
  21751 ) !Value {
  21752     const pt = sema.pt;
  21753     const zcu = pt.zcu;
  21754     if (operand_val.isUndef(zcu)) {
  21755         if (ptr_ty.isAllowzeroPtr(zcu) and ptr_align == .@"1") {
  21756             return pt.undefValue(ptr_ty);
  21757         }
  21758         return sema.failWithUseOfUndef(block, operand_src);
  21759     }
  21760     const addr = try operand_val.toUnsignedIntSema(pt);
  21761     if (!ptr_ty.isAllowzeroPtr(zcu) and addr == 0)
  21762         return sema.fail(block, operand_src, "pointer type '{f}' does not allow address zero", .{ptr_ty.fmt(pt)});
  21763     if (addr != 0 and ptr_align != .none) {
  21764         const masked_addr = if (ptr_ty.childType(zcu).fnPtrMaskOrNull(zcu)) |mask|
  21765             addr & mask
  21766         else
  21767             addr;
  21768 
  21769         if (!ptr_align.check(masked_addr)) {
  21770             return sema.fail(block, operand_src, "pointer type '{f}' requires aligned address", .{ptr_ty.fmt(pt)});
  21771         }
  21772     }
  21773 
  21774     return switch (ptr_ty.zigTypeTag(zcu)) {
  21775         .optional => Value.fromInterned(try pt.intern(.{ .opt = .{
  21776             .ty = ptr_ty.toIntern(),
  21777             .val = if (addr == 0) .none else (try pt.ptrIntValue(ptr_ty.childType(zcu), addr)).toIntern(),
  21778         } })),
  21779         .pointer => try pt.ptrIntValue(ptr_ty, addr),
  21780         else => unreachable,
  21781     };
  21782 }
  21783 
  21784 fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21785     const pt = sema.pt;
  21786     const zcu = pt.zcu;
  21787     const ip = &zcu.intern_pool;
  21788     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  21789     const src = block.nodeOffset(extra.node);
  21790     const operand_src = block.builtinCallArgSrc(extra.node, 0);
  21791     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast");
  21792     const operand = try sema.resolveInst(extra.rhs);
  21793     const operand_ty = sema.typeOf(operand);
  21794 
  21795     const dest_tag = dest_ty.zigTypeTag(zcu);
  21796     const operand_tag = operand_ty.zigTypeTag(zcu);
  21797 
  21798     if (dest_tag != .error_set and dest_tag != .error_union) {
  21799         return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)});
  21800     }
  21801     if (operand_tag != .error_set and operand_tag != .error_union) {
  21802         return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(operand_tag)});
  21803     }
  21804     if (dest_tag == .error_set and operand_tag == .error_union) {
  21805         return sema.fail(block, src, "cannot cast an error union type to error set", .{});
  21806     }
  21807     if (dest_tag == .error_union and operand_tag == .error_union and
  21808         dest_ty.errorUnionPayload(zcu).toIntern() != operand_ty.errorUnionPayload(zcu).toIntern())
  21809     {
  21810         return sema.failWithOwnedErrorMsg(block, msg: {
  21811             const msg = try sema.errMsg(src, "payload types of error unions must match", .{});
  21812             errdefer msg.destroy(sema.gpa);
  21813             const dest_payload_ty = dest_ty.errorUnionPayload(zcu);
  21814             const operand_payload_ty = operand_ty.errorUnionPayload(zcu);
  21815             try sema.errNote(src, msg, "destination payload is '{f}'", .{dest_payload_ty.fmt(pt)});
  21816             try sema.errNote(src, msg, "operand payload is '{f}'", .{operand_payload_ty.fmt(pt)});
  21817             try addDeclaredHereNote(sema, msg, dest_ty);
  21818             try addDeclaredHereNote(sema, msg, operand_ty);
  21819             break :msg msg;
  21820         });
  21821     }
  21822     const dest_err_ty = switch (dest_tag) {
  21823         .error_union => dest_ty.errorUnionSet(zcu),
  21824         .error_set => dest_ty,
  21825         else => unreachable,
  21826     };
  21827     const operand_err_ty = switch (operand_tag) {
  21828         .error_union => operand_ty.errorUnionSet(zcu),
  21829         .error_set => operand_ty,
  21830         else => unreachable,
  21831     };
  21832 
  21833     const disjoint = disjoint: {
  21834         // Try avoiding resolving inferred error sets if we can
  21835         if (!dest_err_ty.isAnyError(zcu) and dest_err_ty.errorSetIsEmpty(zcu)) break :disjoint true;
  21836         if (!operand_err_ty.isAnyError(zcu) and operand_err_ty.errorSetIsEmpty(zcu)) break :disjoint true;
  21837         if (dest_err_ty.isAnyError(zcu)) break :disjoint false;
  21838         if (operand_err_ty.isAnyError(zcu)) break :disjoint false;
  21839         const dest_err_names = dest_err_ty.errorSetNames(zcu);
  21840         for (0..dest_err_names.len) |dest_err_index| {
  21841             if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
  21842                 break :disjoint false;
  21843         }
  21844 
  21845         if (!ip.isInferredErrorSetType(dest_err_ty.toIntern()) and
  21846             !ip.isInferredErrorSetType(operand_err_ty.toIntern()))
  21847         {
  21848             break :disjoint true;
  21849         }
  21850 
  21851         _ = try sema.resolveInferredErrorSetTy(block, src, dest_err_ty.toIntern());
  21852         _ = try sema.resolveInferredErrorSetTy(block, operand_src, operand_err_ty.toIntern());
  21853         for (0..dest_err_names.len) |dest_err_index| {
  21854             if (Type.errorSetHasFieldIp(ip, operand_err_ty.toIntern(), dest_err_names.get(ip)[dest_err_index]))
  21855                 break :disjoint false;
  21856         }
  21857 
  21858         break :disjoint true;
  21859     };
  21860     if (disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) {
  21861         return sema.fail(block, src, "error sets '{f}' and '{f}' have no common errors", .{
  21862             operand_err_ty.fmt(pt), dest_err_ty.fmt(pt),
  21863         });
  21864     }
  21865 
  21866     // operand must be defined since it can be an invalid error value
  21867     if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
  21868         const err_name: InternPool.NullTerminatedString = switch (operand_tag) {
  21869             .error_set => ip.indexToKey(operand_val.toIntern()).err.name,
  21870             .error_union => switch (ip.indexToKey(operand_val.toIntern()).error_union.val) {
  21871                 .err_name => |name| name,
  21872                 .payload => |payload_val| {
  21873                     assert(dest_tag == .error_union); // should be guaranteed from the type checks above
  21874                     return sema.coerce(block, dest_ty, Air.internedToRef(payload_val), operand_src);
  21875                 },
  21876             },
  21877             else => unreachable,
  21878         };
  21879 
  21880         if (!dest_err_ty.isAnyError(zcu) and !Type.errorSetHasFieldIp(ip, dest_err_ty.toIntern(), err_name)) {
  21881             return sema.fail(block, src, "'error.{f}' not a member of error set '{f}'", .{
  21882                 err_name.fmt(ip), dest_err_ty.fmt(pt),
  21883             });
  21884         }
  21885 
  21886         return Air.internedToRef(try pt.intern(switch (dest_tag) {
  21887             .error_set => .{ .err = .{
  21888                 .ty = dest_ty.toIntern(),
  21889                 .name = err_name,
  21890             } },
  21891             .error_union => .{ .error_union = .{
  21892                 .ty = dest_ty.toIntern(),
  21893                 .val = .{ .err_name = err_name },
  21894             } },
  21895             else => unreachable,
  21896         }));
  21897     }
  21898 
  21899     const err_int_ty = try pt.errorIntType();
  21900     if (block.wantSafety() and !dest_err_ty.isAnyError(zcu) and
  21901         dest_err_ty.toIntern() != .adhoc_inferred_error_set_type and
  21902         zcu.backendSupportsFeature(.error_set_has_value))
  21903     {
  21904         const err_code_inst = switch (operand_tag) {
  21905             .error_set => operand,
  21906             .error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand),
  21907             else => unreachable,
  21908         };
  21909         const err_int_inst = try block.addBitCast(err_int_ty, err_code_inst);
  21910 
  21911         if (dest_tag == .error_union) {
  21912             const zero_err = try pt.intRef(err_int_ty, 0);
  21913             const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err);
  21914             if (disjoint) {
  21915                 // Error must be zero.
  21916                 try sema.addSafetyCheck(block, src, is_zero, .invalid_error_code);
  21917             } else {
  21918                 // Error must be in destination set or zero.
  21919                 const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
  21920                 const ok = try block.addBinOp(.bool_or, has_value, is_zero);
  21921                 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
  21922             }
  21923         } else {
  21924             const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
  21925             try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
  21926         }
  21927     }
  21928 
  21929     if (operand_tag == .error_set and dest_tag == .error_union) {
  21930         const err_val = try block.addBitCast(dest_err_ty, operand);
  21931         return block.addTyOp(.wrap_errunion_err, dest_ty, err_val);
  21932     } else {
  21933         return block.addBitCast(dest_ty, operand);
  21934     }
  21935 }
  21936 
  21937 fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  21938     const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
  21939     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small)));
  21940     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  21941     const src = block.nodeOffset(extra.node);
  21942     const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node });
  21943     const operand = try sema.resolveInst(extra.rhs);
  21944     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName());
  21945     return sema.ptrCastFull(
  21946         block,
  21947         flags,
  21948         src,
  21949         operand,
  21950         operand_src,
  21951         dest_ty,
  21952         flags.needResultTypeBuiltinName(),
  21953     );
  21954 }
  21955 
  21956 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  21957     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  21958     const src = block.nodeOffset(inst_data.src_node);
  21959     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  21960     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  21961     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrCast");
  21962     const operand = try sema.resolveInst(extra.rhs);
  21963 
  21964     return sema.ptrCastFull(
  21965         block,
  21966         .{ .ptr_cast = true },
  21967         src,
  21968         operand,
  21969         operand_src,
  21970         dest_ty,
  21971         "@ptrCast",
  21972     );
  21973 }
  21974 
  21975 fn ptrCastFull(
  21976     sema: *Sema,
  21977     block: *Block,
  21978     flags: Zir.Inst.FullPtrCastFlags,
  21979     src: LazySrcLoc,
  21980     operand: Air.Inst.Ref,
  21981     operand_src: LazySrcLoc,
  21982     dest_ty: Type,
  21983     operation: []const u8,
  21984 ) CompileError!Air.Inst.Ref {
  21985     const pt = sema.pt;
  21986     const zcu = pt.zcu;
  21987     const operand_ty = sema.typeOf(operand);
  21988 
  21989     try sema.checkPtrType(block, src, dest_ty, true);
  21990     try sema.checkPtrOperand(block, operand_src, operand_ty);
  21991 
  21992     const src_info = operand_ty.ptrInfo(zcu);
  21993     const dest_info = dest_ty.ptrInfo(zcu);
  21994 
  21995     try Type.fromInterned(src_info.child).resolveLayout(pt);
  21996     try Type.fromInterned(dest_info.child).resolveLayout(pt);
  21997 
  21998     const DestSliceLen = union(enum) {
  21999         undef,
  22000         constant: u64,
  22001         equal_runtime_src_slice,
  22002         change_runtime_src_slice: struct {
  22003             bytes_per_src: u64,
  22004             bytes_per_dest: u64,
  22005         },
  22006     };
  22007     // Populated iff the destination type is a slice.
  22008     const dest_slice_len: ?DestSliceLen = len: {
  22009         switch (dest_info.flags.size) {
  22010             .slice => {},
  22011             .many, .c, .one => break :len null,
  22012         }
  22013         // A `null` length means the operand is a runtime-known slice (so the length is runtime-known).
  22014         // `src_elem_type` is different from `src_info.child` if the latter is an array, to ensure we ignore sentinels.
  22015         const src_elem_ty: Type, const opt_src_len: ?u64 = switch (src_info.flags.size) {
  22016             .one => src: {
  22017                 const true_child: Type = .fromInterned(src_info.child);
  22018                 break :src switch (true_child.zigTypeTag(zcu)) {
  22019                     .array => .{ true_child.childType(zcu), true_child.arrayLen(zcu) },
  22020                     else => .{ true_child, 1 },
  22021                 };
  22022             },
  22023             .slice => src: {
  22024                 const operand_val = try sema.resolveValue(operand) orelse break :src .{ .fromInterned(src_info.child), null };
  22025                 if (operand_val.isUndef(zcu)) break :len .undef;
  22026                 const slice_val = switch (operand_ty.zigTypeTag(zcu)) {
  22027                     .optional => operand_val.optionalValue(zcu) orelse break :len .undef,
  22028                     .pointer => operand_val,
  22029                     else => unreachable,
  22030                 };
  22031                 const slice_len_resolved = try sema.resolveLazyValue(.fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern())));
  22032                 if (slice_len_resolved.isUndef(zcu)) break :len .undef;
  22033                 break :src .{ .fromInterned(src_info.child), slice_len_resolved.toUnsignedInt(zcu) };
  22034             },
  22035             .many, .c => {
  22036                 return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)});
  22037             },
  22038         };
  22039         const dest_elem_ty: Type = .fromInterned(dest_info.child);
  22040         if (dest_elem_ty.toIntern() == src_elem_ty.toIntern()) {
  22041             break :len if (opt_src_len) |l| .{ .constant = l } else .equal_runtime_src_slice;
  22042         }
  22043         if (!src_elem_ty.comptimeOnly(zcu) and !dest_elem_ty.comptimeOnly(zcu)) {
  22044             const src_elem_size = src_elem_ty.abiSize(zcu);
  22045             const dest_elem_size = dest_elem_ty.abiSize(zcu);
  22046             if (dest_elem_size == 0) {
  22047                 return sema.fail(block, src, "cannot infer length of slice of zero-bit '{f}' from '{f}'", .{
  22048                     dest_elem_ty.fmt(pt), operand_ty.fmt(pt),
  22049                 });
  22050             }
  22051             if (opt_src_len) |src_len| {
  22052                 const bytes = src_len * src_elem_size;
  22053                 const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch switch (src_info.flags.size) {
  22054                     .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}),
  22055                     .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{Type.fromInterned(src_info.child).fmt(pt)}),
  22056                     else => unreachable,
  22057                 };
  22058                 break :len .{ .constant = dest_len };
  22059             }
  22060             assert(src_info.flags.size == .slice);
  22061             break :len .{ .change_runtime_src_slice = .{
  22062                 .bytes_per_src = src_elem_size,
  22063                 .bytes_per_dest = dest_elem_size,
  22064             } };
  22065         }
  22066         // We apply rules for comptime memory consistent with comptime loads/stores, where arrays of
  22067         // comptime-only types can be "restructured".
  22068         const dest_base_ty: Type, const dest_base_per_elem: u64 = dest_elem_ty.arrayBase(zcu);
  22069         const src_base_ty: Type, const src_base_per_elem: u64 = src_elem_ty.arrayBase(zcu);
  22070         // The source value has `src_len * src_base_per_elem` values of type `src_base_ty`.
  22071         // The result value will have `dest_len * dest_base_per_elem` values of type `dest_base_ty`.
  22072         if (dest_base_ty.toIntern() != src_base_ty.toIntern()) {
  22073             return sema.fail(block, src, "cannot infer length of comptime-only '{f}' from incompatible '{f}'", .{
  22074                 dest_ty.fmt(pt), operand_ty.fmt(pt),
  22075             });
  22076         }
  22077         // `src_base_ty` is comptime-only, so `src_elem_ty` is comptime-only, so `operand_ty` is
  22078         // comptime-only, so `operand` is comptime-known, so `opt_src_len` is non-`null`.
  22079         const src_len = opt_src_len.?;
  22080         const base_len = src_len * src_base_per_elem;
  22081         const dest_len = std.math.divExact(u64, base_len, dest_base_per_elem) catch switch (src_info.flags.size) {
  22082             .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}),
  22083             .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{src_elem_ty.fmt(pt)}),
  22084             else => unreachable,
  22085         };
  22086         break :len .{ .constant = dest_len };
  22087     };
  22088 
  22089     // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs
  22090 
  22091     if (!flags.ptr_cast) {
  22092         const is_array_ptr_to_slice = b: {
  22093             if (dest_info.flags.size != .slice) break :b false;
  22094             if (src_info.flags.size != .one) break :b false;
  22095             const src_pointer_child: Type = .fromInterned(src_info.child);
  22096             if (src_pointer_child.zigTypeTag(zcu) != .array) break :b false;
  22097             const src_elem = src_pointer_child.childType(zcu);
  22098             break :b src_elem.toIntern() == dest_info.child;
  22099         };
  22100 
  22101         check_size: {
  22102             if (src_info.flags.size == dest_info.flags.size) break :check_size;
  22103             if (is_array_ptr_to_slice) break :check_size;
  22104             if (src_info.flags.size == .c) break :check_size;
  22105             if (dest_info.flags.size == .c) break :check_size;
  22106             return sema.failWithOwnedErrorMsg(block, msg: {
  22107                 const msg = try sema.errMsg(src, "cannot implicitly convert {s} to {s}", .{
  22108                     pointerSizeString(src_info.flags.size),
  22109                     pointerSizeString(dest_info.flags.size),
  22110                 });
  22111                 errdefer msg.destroy(sema.gpa);
  22112                 if (dest_info.flags.size == .many and
  22113                     (src_info.flags.size == .slice or
  22114                         (src_info.flags.size == .one and Type.fromInterned(src_info.child).zigTypeTag(zcu) == .array)))
  22115                 {
  22116                     try sema.errNote(src, msg, "use 'ptr' field to convert slice to many pointer", .{});
  22117                 } else {
  22118                     try sema.errNote(src, msg, "use @ptrCast to change pointer size", .{});
  22119                 }
  22120                 break :msg msg;
  22121             });
  22122         }
  22123 
  22124         check_child: {
  22125             const src_child: Type = if (dest_info.flags.size == .slice and src_info.flags.size == .one) blk: {
  22126                 // *[n]T -> []T
  22127                 break :blk Type.fromInterned(src_info.child).childType(zcu);
  22128             } else .fromInterned(src_info.child);
  22129 
  22130             const dest_child: Type = .fromInterned(dest_info.child);
  22131 
  22132             const imc_res = try sema.coerceInMemoryAllowed(
  22133                 block,
  22134                 dest_child,
  22135                 src_child,
  22136                 !dest_info.flags.is_const,
  22137                 zcu.getTarget(),
  22138                 src,
  22139                 operand_src,
  22140                 null,
  22141             );
  22142             if (imc_res == .ok) break :check_child;
  22143             return sema.failWithOwnedErrorMsg(block, msg: {
  22144                 const msg = try sema.errMsg(src, "pointer element type '{f}' cannot coerce into element type '{f}'", .{
  22145                     src_child.fmt(pt), dest_child.fmt(pt),
  22146                 });
  22147                 errdefer msg.destroy(sema.gpa);
  22148                 try imc_res.report(sema, src, msg);
  22149                 try sema.errNote(src, msg, "use @ptrCast to cast pointer element type", .{});
  22150                 break :msg msg;
  22151             });
  22152         }
  22153 
  22154         check_sent: {
  22155             if (dest_info.sentinel == .none) break :check_sent;
  22156             if (src_info.flags.size == .c) break :check_sent;
  22157             if (src_info.sentinel != .none) {
  22158                 const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_info.sentinel, dest_info.child);
  22159                 if (dest_info.sentinel == coerced_sent) break :check_sent;
  22160             }
  22161             if (is_array_ptr_to_slice) {
  22162                 // [*]nT -> []T
  22163                 const arr_ty: Type = .fromInterned(src_info.child);
  22164                 if (arr_ty.sentinel(zcu)) |src_sentinel| {
  22165                     const coerced_sent = try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, src_sentinel.toIntern(), dest_info.child);
  22166                     if (dest_info.sentinel == coerced_sent) break :check_sent;
  22167                 }
  22168             }
  22169             return sema.failWithOwnedErrorMsg(block, msg: {
  22170                 const msg = if (src_info.sentinel == .none) blk: {
  22171                     break :blk try sema.errMsg(src, "destination pointer requires '{f}' sentinel", .{
  22172                         Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema),
  22173                     });
  22174                 } else blk: {
  22175                     break :blk try sema.errMsg(src, "pointer sentinel '{f}' cannot coerce into pointer sentinel '{f}'", .{
  22176                         Value.fromInterned(src_info.sentinel).fmtValueSema(pt, sema),
  22177                         Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema),
  22178                     });
  22179                 };
  22180                 errdefer msg.destroy(sema.gpa);
  22181                 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{});
  22182                 break :msg msg;
  22183             });
  22184         }
  22185 
  22186         if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) {
  22187             return sema.failWithOwnedErrorMsg(block, msg: {
  22188                 const msg = try sema.errMsg(src, "pointer host size '{d}' cannot coerce into pointer host size '{d}'", .{
  22189                     src_info.packed_offset.host_size,
  22190                     dest_info.packed_offset.host_size,
  22191                 });
  22192                 errdefer msg.destroy(sema.gpa);
  22193                 try sema.errNote(src, msg, "use @ptrCast to cast pointer host size", .{});
  22194                 break :msg msg;
  22195             });
  22196         }
  22197 
  22198         if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) {
  22199             return sema.failWithOwnedErrorMsg(block, msg: {
  22200                 const msg = try sema.errMsg(src, "pointer bit offset '{d}' cannot coerce into pointer bit offset '{d}'", .{
  22201                     src_info.packed_offset.bit_offset,
  22202                     dest_info.packed_offset.bit_offset,
  22203                 });
  22204                 errdefer msg.destroy(sema.gpa);
  22205                 try sema.errNote(src, msg, "use @ptrCast to cast pointer bit offset", .{});
  22206                 break :msg msg;
  22207             });
  22208         }
  22209 
  22210         check_allowzero: {
  22211             const src_allows_zero = operand_ty.ptrAllowsZero(zcu);
  22212             const dest_allows_zero = dest_ty.ptrAllowsZero(zcu);
  22213             if (!src_allows_zero) break :check_allowzero;
  22214             if (dest_allows_zero) break :check_allowzero;
  22215 
  22216             return sema.failWithOwnedErrorMsg(block, msg: {
  22217                 const msg = try sema.errMsg(src, "'{f}' could have null values which are illegal in type '{f}'", .{
  22218                     operand_ty.fmt(pt),
  22219                     dest_ty.fmt(pt),
  22220                 });
  22221                 errdefer msg.destroy(sema.gpa);
  22222                 try sema.errNote(src, msg, "use @ptrCast to assert the pointer is not null", .{});
  22223                 break :msg msg;
  22224             });
  22225         }
  22226 
  22227         // TODO: vector index?
  22228     }
  22229 
  22230     const src_align = if (src_info.flags.alignment != .none)
  22231         src_info.flags.alignment
  22232     else
  22233         Type.fromInterned(src_info.child).abiAlignment(zcu);
  22234 
  22235     const dest_align = if (dest_info.flags.alignment != .none)
  22236         dest_info.flags.alignment
  22237     else
  22238         Type.fromInterned(dest_info.child).abiAlignment(zcu);
  22239 
  22240     if (!flags.align_cast) {
  22241         if (dest_align.compare(.gt, src_align)) {
  22242             return sema.failWithOwnedErrorMsg(block, msg: {
  22243                 const msg = try sema.errMsg(src, "{s} increases pointer alignment", .{operation});
  22244                 errdefer msg.destroy(sema.gpa);
  22245                 try sema.errNote(operand_src, msg, "'{f}' has alignment '{d}'", .{
  22246                     operand_ty.fmt(pt), src_align.toByteUnits() orelse 0,
  22247                 });
  22248                 try sema.errNote(src, msg, "'{f}' has alignment '{d}'", .{
  22249                     dest_ty.fmt(pt), dest_align.toByteUnits() orelse 0,
  22250                 });
  22251                 try sema.errNote(src, msg, "use @alignCast to assert pointer alignment", .{});
  22252                 break :msg msg;
  22253             });
  22254         }
  22255     }
  22256 
  22257     if (!flags.addrspace_cast) {
  22258         if (src_info.flags.address_space != dest_info.flags.address_space) {
  22259             return sema.failWithOwnedErrorMsg(block, msg: {
  22260                 const msg = try sema.errMsg(src, "{s} changes pointer address space", .{operation});
  22261                 errdefer msg.destroy(sema.gpa);
  22262                 try sema.errNote(operand_src, msg, "'{f}' has address space '{s}'", .{
  22263                     operand_ty.fmt(pt), @tagName(src_info.flags.address_space),
  22264                 });
  22265                 try sema.errNote(src, msg, "'{f}' has address space '{s}'", .{
  22266                     dest_ty.fmt(pt), @tagName(dest_info.flags.address_space),
  22267                 });
  22268                 try sema.errNote(src, msg, "use @addrSpaceCast to cast pointer address space", .{});
  22269                 break :msg msg;
  22270             });
  22271         }
  22272     } else {
  22273         // Some address space casts are always disallowed
  22274         if (!target_util.addrSpaceCastIsValid(zcu.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) {
  22275             return sema.failWithOwnedErrorMsg(block, msg: {
  22276                 const msg = try sema.errMsg(src, "invalid address space cast", .{});
  22277                 errdefer msg.destroy(sema.gpa);
  22278                 try sema.errNote(operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{
  22279                     @tagName(src_info.flags.address_space),
  22280                     @tagName(dest_info.flags.address_space),
  22281                 });
  22282                 break :msg msg;
  22283             });
  22284         }
  22285     }
  22286 
  22287     if (!flags.const_cast) {
  22288         if (src_info.flags.is_const and !dest_info.flags.is_const) {
  22289             return sema.failWithOwnedErrorMsg(block, msg: {
  22290                 const msg = try sema.errMsg(src, "{s} discards const qualifier", .{operation});
  22291                 errdefer msg.destroy(sema.gpa);
  22292                 try sema.errNote(src, msg, "use @constCast to discard const qualifier", .{});
  22293                 break :msg msg;
  22294             });
  22295         }
  22296     }
  22297 
  22298     if (!flags.volatile_cast) {
  22299         if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) {
  22300             return sema.failWithOwnedErrorMsg(block, msg: {
  22301                 const msg = try sema.errMsg(src, "{s} discards volatile qualifier", .{operation});
  22302                 errdefer msg.destroy(sema.gpa);
  22303                 try sema.errNote(src, msg, "use @volatileCast to discard volatile qualifier", .{});
  22304                 break :msg msg;
  22305             });
  22306         }
  22307     }
  22308 
  22309     // Type validation done -- this cast is okay. Let's do it!
  22310     //
  22311     // `operand` is a maybe-optional pointer or slice.
  22312     // `dest_ty` is a maybe-optional pointer or slice.
  22313     //
  22314     // We have a few safety checks:
  22315     // * if the destination does not allow zero, check the operand is not null / 0
  22316     // * if the destination is more aligned than the operand, check the pointer alignment
  22317     // * if `slice_needs_len_change`, check the element count divides neatly
  22318 
  22319     ct: {
  22320         if (flags.addrspace_cast) break :ct; // cannot `@addrSpaceCast` at comptime
  22321         const operand_val = try sema.resolveValue(operand) orelse break :ct;
  22322 
  22323         if (operand_val.isUndef(zcu)) {
  22324             if (!dest_ty.ptrAllowsZero(zcu)) {
  22325                 return sema.failWithUseOfUndef(block, operand_src);
  22326             }
  22327             return pt.undefRef(dest_ty);
  22328         }
  22329 
  22330         if (operand_val.isNull(zcu)) {
  22331             if (!dest_ty.ptrAllowsZero(zcu)) {
  22332                 return sema.fail(block, operand_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)});
  22333             }
  22334             if (dest_ty.zigTypeTag(zcu) == .optional) {
  22335                 return Air.internedToRef((try pt.nullValue(dest_ty)).toIntern());
  22336             } else {
  22337                 return Air.internedToRef((try pt.ptrIntValue(dest_ty, 0)).toIntern());
  22338             }
  22339         }
  22340 
  22341         const ptr_val: Value = switch (src_info.flags.size) {
  22342             .slice => .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.ptr),
  22343             .one, .many, .c => operand_val,
  22344         };
  22345 
  22346         if (dest_align.compare(.gt, src_align)) {
  22347             if (try ptr_val.getUnsignedIntSema(pt)) |addr| {
  22348                 const masked_addr = if (Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu)) |mask|
  22349                     addr & mask
  22350                 else
  22351                     addr;
  22352 
  22353                 if (!dest_align.check(masked_addr)) {
  22354                     return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{
  22355                         addr,
  22356                         dest_align.toByteUnits().?,
  22357                     });
  22358                 }
  22359             }
  22360         }
  22361 
  22362         if (dest_info.flags.size == .slice) {
  22363             // Because the operand is comptime-known and not `null`, the slice length has already been computed:
  22364             const len: Value = switch (dest_slice_len.?) {
  22365                 .undef => .undef_usize,
  22366                 .constant => |n| try pt.intValue(.usize, n),
  22367                 .equal_runtime_src_slice => unreachable,
  22368                 .change_runtime_src_slice => unreachable,
  22369             };
  22370             return Air.internedToRef(try pt.intern(.{ .slice = .{
  22371                 .ty = dest_ty.toIntern(),
  22372                 .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(),
  22373                 .len = len.toIntern(),
  22374             } }));
  22375         } else {
  22376             // Any to non-slice
  22377             const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty);
  22378             return Air.internedToRef(new_ptr_val.toIntern());
  22379         }
  22380     }
  22381 
  22382     try sema.validateRuntimeValue(block, operand_src, operand);
  22383 
  22384     const can_cast_to_int = !target_util.arePointersLogical(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));
  22385     const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu);
  22386     const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align);
  22387 
  22388     const slice_needs_len_change = if (dest_slice_len) |l| switch (l) {
  22389         .undef, .equal_runtime_src_slice => false,
  22390         .constant, .change_runtime_src_slice => true,
  22391     } else false;
  22392 
  22393     // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer.
  22394     const need_operand_ptr = src_info.flags.size != .slice or // we already have it
  22395         dest_info.flags.size != .slice or // the result is a raw pointer
  22396         need_null_check or // safety check happens on pointer
  22397         need_align_check or // safety check happens on pointer
  22398         flags.addrspace_cast or // AIR addrspace_cast acts on a pointer
  22399         slice_needs_len_change; // to change the length, we reconstruct the slice
  22400 
  22401     // This is not quite just the pointer part of `operand` -- it's also had the address space cast done already.
  22402     const operand_ptr: Air.Inst.Ref = ptr: {
  22403         if (!need_operand_ptr) break :ptr .none;
  22404         // First, just get the pointer.
  22405         const pre_addrspace_cast = inner: {
  22406             if (src_info.flags.size != .slice) break :inner operand;
  22407             if (operand_ty.zigTypeTag(zcu) == .optional) {
  22408                 break :inner try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty);
  22409             } else {
  22410                 break :inner try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty);
  22411             }
  22412         };
  22413         // Now, do an addrspace cast if necessary!
  22414         if (!flags.addrspace_cast) break :ptr pre_addrspace_cast;
  22415 
  22416         const intermediate_ptr_ty = try pt.ptrTypeSema(info: {
  22417             var info = src_info;
  22418             info.flags.address_space = dest_info.flags.address_space;
  22419             break :info info;
  22420         });
  22421         const intermediate_ty = if (operand_ty.zigTypeTag(zcu) == .optional) blk: {
  22422             break :blk try pt.optionalType(intermediate_ptr_ty.toIntern());
  22423         } else intermediate_ptr_ty;
  22424         break :ptr try block.addInst(.{
  22425             .tag = .addrspace_cast,
  22426             .data = .{ .ty_op = .{
  22427                 .ty = Air.internedToRef(intermediate_ty.toIntern()),
  22428                 .operand = pre_addrspace_cast,
  22429             } },
  22430         });
  22431     };
  22432 
  22433     // Whether we need to know if the (slice) operand has `len == 0`.
  22434     const need_operand_len_is_zero = src_info.flags.size == .slice and
  22435         dest_info.flags.size == .slice and
  22436         (need_null_check or need_align_check);
  22437     // Whether we need to get the (slice) operand's `len`.
  22438     const need_operand_len = need_len: {
  22439         if (src_info.flags.size != .slice) break :need_len false;
  22440         if (dest_info.flags.size != .slice) break :need_len false;
  22441         if (need_operand_len_is_zero) break :need_len true;
  22442         if (flags.addrspace_cast or slice_needs_len_change) break :need_len true;
  22443         break :need_len false;
  22444     };
  22445     // `.none` if `!need_operand_len`.
  22446     const operand_len: Air.Inst.Ref = len: {
  22447         if (!need_operand_len) break :len .none;
  22448         break :len try block.addTyOp(.slice_len, .usize, operand);
  22449     };
  22450     // `.none` if `!need_operand_len_is_zero`.
  22451     const operand_len_is_zero: Air.Inst.Ref = zero: {
  22452         if (!need_operand_len_is_zero) break :zero .none;
  22453         assert(need_operand_len);
  22454         break :zero try block.addBinOp(.cmp_eq, operand_len, .zero_usize);
  22455     };
  22456 
  22457     // `operand_ptr` converted to an integer, for safety checks.
  22458     const operand_ptr_int: Air.Inst.Ref = if (need_null_check or need_align_check) i: {
  22459         assert(need_operand_ptr);
  22460         break :i try block.addBitCast(.usize, operand_ptr);
  22461     } else .none;
  22462 
  22463     if (need_null_check) {
  22464         assert(operand_ptr_int != .none);
  22465         const ptr_is_non_zero = try block.addBinOp(.cmp_neq, operand_ptr_int, .zero_usize);
  22466         const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: {
  22467             break :ok try block.addBinOp(.bool_or, operand_len_is_zero, ptr_is_non_zero);
  22468         } else ptr_is_non_zero;
  22469         try sema.addSafetyCheck(block, src, ok, .cast_to_null);
  22470     }
  22471     if (need_align_check) {
  22472         assert(operand_ptr_int != .none);
  22473         const align_mask = try pt.intRef(.usize, mask: {
  22474             const target_ptr_mask = Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu) orelse ~@as(u64, 0);
  22475             break :mask (dest_align.toByteUnits().? - 1) & target_ptr_mask;
  22476         });
  22477         const ptr_masked = try block.addBinOp(.bit_and, operand_ptr_int, align_mask);
  22478         const is_aligned = try block.addBinOp(.cmp_eq, ptr_masked, .zero_usize);
  22479         const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: {
  22480             break :ok try block.addBinOp(.bool_or, operand_len_is_zero, is_aligned);
  22481         } else is_aligned;
  22482         try sema.addSafetyCheck(block, src, ok, .incorrect_alignment);
  22483     }
  22484 
  22485     if (dest_info.flags.size == .slice) {
  22486         if (src_info.flags.size == .slice and !flags.addrspace_cast and !slice_needs_len_change) {
  22487             // Fast path: just bitcast!
  22488             return block.addBitCast(dest_ty, operand);
  22489         }
  22490 
  22491         // We need to deconstruct the slice (if applicable) and reconstruct it.
  22492         assert(need_operand_ptr);
  22493 
  22494         const result_len: Air.Inst.Ref = switch (dest_slice_len.?) {
  22495             .undef => .undef_usize,
  22496             .constant => |n| try pt.intRef(.usize, n),
  22497             .equal_runtime_src_slice => len: {
  22498                 assert(need_operand_len);
  22499                 break :len operand_len;
  22500             },
  22501             .change_runtime_src_slice => |change| len: {
  22502                 assert(need_operand_len);
  22503                 // If `mul / div` is a whole number, then just multiply the length by it.
  22504                 if (std.math.divExact(u64, change.bytes_per_src, change.bytes_per_dest)) |dest_per_src| {
  22505                     const multiplier = try pt.intRef(.usize, dest_per_src);
  22506                     break :len try block.addBinOp(.mul, operand_len, multiplier);
  22507                 } else |err| switch (err) {
  22508                     error.DivisionByZero => unreachable,
  22509                     error.UnexpectedRemainder => {}, // fall through to code below
  22510                 }
  22511                 // If `div / mul` is a whole number, then just divide the length by it.
  22512                 // This incurs a safety check.
  22513                 if (std.math.divExact(u64, change.bytes_per_dest, change.bytes_per_src)) |src_per_dest| {
  22514                     const divisor = try pt.intRef(.usize, src_per_dest);
  22515                     if (block.wantSafety()) {
  22516                         // Check that the element count divides neatly.
  22517                         const remainder = try block.addBinOp(.rem, operand_len, divisor);
  22518                         const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  22519                         try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len});
  22520                     }
  22521                     break :len try block.addBinOp(.div_exact, operand_len, divisor);
  22522                 } else |err| switch (err) {
  22523                     error.DivisionByZero => unreachable,
  22524                     error.UnexpectedRemainder => {}, // fall through to code below
  22525                 }
  22526                 // Fallback: the elements don't divide easily. We'll multiply *and* divide. This incurs a safety check.
  22527                 const total_bytes_ref = try block.addBinOp(.mul, operand_len, try pt.intRef(.usize, change.bytes_per_src));
  22528                 const bytes_per_dest_ref = try pt.intRef(.usize, change.bytes_per_dest);
  22529                 if (block.wantSafety()) {
  22530                     // Check that `total_bytes_ref` divides neatly into `bytes_per_dest_ref`.
  22531                     const remainder = try block.addBinOp(.rem, total_bytes_ref, bytes_per_dest_ref);
  22532                     const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
  22533                     try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len});
  22534                 }
  22535                 break :len try block.addBinOp(.div_exact, total_bytes_ref, bytes_per_dest_ref);
  22536             },
  22537         };
  22538 
  22539         const operand_ptr_ty = sema.typeOf(operand_ptr);
  22540         const want_ptr_ty = switch (dest_ty.zigTypeTag(zcu)) {
  22541             .optional => try pt.optionalType(dest_ty.childType(zcu).slicePtrFieldType(zcu).toIntern()),
  22542             .pointer => dest_ty.slicePtrFieldType(zcu),
  22543             else => unreachable,
  22544         };
  22545         const coerced_ptr = if (operand_ptr_ty.toIntern() != want_ptr_ty.toIntern()) ptr: {
  22546             break :ptr try block.addBitCast(want_ptr_ty, operand_ptr);
  22547         } else operand_ptr;
  22548 
  22549         return block.addInst(.{
  22550             .tag = .slice,
  22551             .data = .{ .ty_pl = .{
  22552                 .ty = Air.internedToRef(dest_ty.toIntern()),
  22553                 .payload = try sema.addExtra(Air.Bin{
  22554                     .lhs = coerced_ptr,
  22555                     .rhs = result_len,
  22556                 }),
  22557             } },
  22558         });
  22559     } else {
  22560         assert(need_operand_ptr);
  22561         // We just need to bitcast the pointer, if necessary.
  22562         // It might not be necessary, since we might have just needed the `addrspace_cast`.
  22563         const result = if (sema.typeOf(operand_ptr).toIntern() == dest_ty.toIntern())
  22564             operand_ptr
  22565         else
  22566             try block.addBitCast(dest_ty, operand_ptr);
  22567 
  22568         try sema.checkKnownAllocPtr(block, operand, result);
  22569         return result;
  22570     }
  22571 }
  22572 
  22573 fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  22574     const pt = sema.pt;
  22575     const zcu = pt.zcu;
  22576     const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
  22577     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small)));
  22578     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  22579     const src = block.nodeOffset(extra.node);
  22580     const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node });
  22581     const operand = try sema.resolveInst(extra.operand);
  22582     const operand_ty = sema.typeOf(operand);
  22583     try sema.checkPtrOperand(block, operand_src, operand_ty);
  22584 
  22585     var ptr_info = operand_ty.ptrInfo(zcu);
  22586     if (flags.const_cast) ptr_info.flags.is_const = false;
  22587     if (flags.volatile_cast) ptr_info.flags.is_volatile = false;
  22588 
  22589     const dest_ty = blk: {
  22590         const dest_ty = try pt.ptrTypeSema(ptr_info);
  22591         if (operand_ty.zigTypeTag(zcu) == .optional) {
  22592             break :blk try pt.optionalType(dest_ty.toIntern());
  22593         }
  22594         break :blk dest_ty;
  22595     };
  22596 
  22597     if (try sema.resolveValue(operand)) |operand_val| {
  22598         return Air.internedToRef((try pt.getCoerced(operand_val, dest_ty)).toIntern());
  22599     }
  22600 
  22601     try sema.requireRuntimeBlock(block, src, null);
  22602     const new_ptr = try block.addBitCast(dest_ty, operand);
  22603     try sema.checkKnownAllocPtr(block, operand, new_ptr);
  22604     return new_ptr;
  22605 }
  22606 
  22607 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22608     const pt = sema.pt;
  22609     const zcu = pt.zcu;
  22610     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  22611     const src = block.nodeOffset(inst_data.src_node);
  22612     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22613     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22614     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate");
  22615     const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src);
  22616     const operand = try sema.resolveInst(extra.rhs);
  22617     const operand_ty = sema.typeOf(operand);
  22618     const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
  22619 
  22620     const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector;
  22621     const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector;
  22622     if (operand_is_vector != dest_is_vector) {
  22623         return sema.fail(block, operand_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), operand_ty.fmt(pt) });
  22624     }
  22625 
  22626     if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
  22627         return sema.coerce(block, dest_ty, operand, operand_src);
  22628     }
  22629 
  22630     const dest_info = dest_scalar_ty.intInfo(zcu);
  22631 
  22632     if (try sema.typeHasOnePossibleValue(dest_ty)) |val| {
  22633         return Air.internedToRef(val.toIntern());
  22634     }
  22635 
  22636     if (operand_scalar_ty.zigTypeTag(zcu) != .comptime_int) {
  22637         const operand_info = operand_ty.intInfo(zcu);
  22638         if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22639             return Air.internedToRef(val.toIntern());
  22640         }
  22641 
  22642         if (operand_info.signedness != dest_info.signedness) {
  22643             return sema.fail(block, operand_src, "expected {s} integer type, found '{f}'", .{
  22644                 @tagName(dest_info.signedness), operand_ty.fmt(pt),
  22645             });
  22646         }
  22647         switch (std.math.order(dest_info.bits, operand_info.bits)) {
  22648             .gt => {
  22649                 const msg = msg: {
  22650                     const msg = try sema.errMsg(
  22651                         src,
  22652                         "destination type '{f}' has more bits than source type '{f}'",
  22653                         .{ dest_ty.fmt(pt), operand_ty.fmt(pt) },
  22654                     );
  22655                     errdefer msg.destroy(sema.gpa);
  22656                     try sema.errNote(src, msg, "destination type has {d} bits", .{
  22657                         dest_info.bits,
  22658                     });
  22659                     try sema.errNote(operand_src, msg, "operand type has {d} bits", .{
  22660                         operand_info.bits,
  22661                     });
  22662                     break :msg msg;
  22663                 };
  22664                 return sema.failWithOwnedErrorMsg(block, msg);
  22665             },
  22666             .eq => return operand,
  22667             .lt => {},
  22668         }
  22669     }
  22670 
  22671     if (try sema.resolveValueResolveLazy(operand)) |val| {
  22672         if (val.isUndef(zcu)) return pt.undefRef(dest_ty);
  22673         if (!dest_is_vector) {
  22674             return Air.internedToRef((try pt.getCoerced(
  22675                 try val.intTrunc(operand_ty, sema.arena, dest_info.signedness, dest_info.bits, pt),
  22676                 dest_ty,
  22677             )).toIntern());
  22678         }
  22679         const elems = try sema.arena.alloc(InternPool.Index, operand_ty.vectorLen(zcu));
  22680         for (elems, 0..) |*elem, i| {
  22681             const elem_val = try val.elemValue(pt, i);
  22682             const uncoerced_elem = try elem_val.intTrunc(operand_scalar_ty, sema.arena, dest_info.signedness, dest_info.bits, pt);
  22683             elem.* = (try pt.getCoerced(uncoerced_elem, dest_scalar_ty)).toIntern();
  22684         }
  22685         return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  22686             .ty = dest_ty.toIntern(),
  22687             .storage = .{ .elems = elems },
  22688         } })));
  22689     }
  22690 
  22691     try sema.requireRuntimeBlock(block, src, operand_src);
  22692     return block.addTyOp(.trunc, dest_ty, operand);
  22693 }
  22694 
  22695 fn zirBitCount(
  22696     sema: *Sema,
  22697     block: *Block,
  22698     inst: Zir.Inst.Index,
  22699     air_tag: Air.Inst.Tag,
  22700     comptime comptimeOp: fn (val: Value, ty: Type, zcu: *Zcu) u64,
  22701 ) CompileError!Air.Inst.Ref {
  22702     const pt = sema.pt;
  22703     const zcu = pt.zcu;
  22704     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  22705     const src = block.nodeOffset(inst_data.src_node);
  22706     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22707     const operand = try sema.resolveInst(inst_data.operand);
  22708     const operand_ty = sema.typeOf(operand);
  22709     _ = try sema.checkIntOrVector(block, operand, operand_src);
  22710     const bits = operand_ty.intInfo(zcu).bits;
  22711 
  22712     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22713         return Air.internedToRef(val.toIntern());
  22714     }
  22715 
  22716     const result_scalar_ty = try pt.smallestUnsignedInt(bits);
  22717     switch (operand_ty.zigTypeTag(zcu)) {
  22718         .vector => {
  22719             const vec_len = operand_ty.vectorLen(zcu);
  22720             const result_ty = try pt.vectorType(.{
  22721                 .len = vec_len,
  22722                 .child = result_scalar_ty.toIntern(),
  22723             });
  22724             if (try sema.resolveValue(operand)) |val| {
  22725                 if (val.isUndef(zcu)) return pt.undefRef(result_ty);
  22726 
  22727                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  22728                 const scalar_ty = operand_ty.scalarType(zcu);
  22729                 for (elems, 0..) |*elem, i| {
  22730                     const elem_val = try val.elemValue(pt, i);
  22731                     const count = comptimeOp(elem_val, scalar_ty, zcu);
  22732                     elem.* = (try pt.intValue(result_scalar_ty, count)).toIntern();
  22733                 }
  22734                 return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  22735                     .ty = result_ty.toIntern(),
  22736                     .storage = .{ .elems = elems },
  22737                 } })));
  22738             } else {
  22739                 try sema.requireRuntimeBlock(block, src, operand_src);
  22740                 return block.addTyOp(air_tag, result_ty, operand);
  22741             }
  22742         },
  22743         .int => {
  22744             if (try sema.resolveValueResolveLazy(operand)) |val| {
  22745                 if (val.isUndef(zcu)) return pt.undefRef(result_scalar_ty);
  22746                 return pt.intRef(result_scalar_ty, comptimeOp(val, operand_ty, zcu));
  22747             } else {
  22748                 try sema.requireRuntimeBlock(block, src, operand_src);
  22749                 return block.addTyOp(air_tag, result_scalar_ty, operand);
  22750             }
  22751         },
  22752         else => unreachable,
  22753     }
  22754 }
  22755 
  22756 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22757     const pt = sema.pt;
  22758     const zcu = pt.zcu;
  22759     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  22760     const src = block.nodeOffset(inst_data.src_node);
  22761     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22762     const operand = try sema.resolveInst(inst_data.operand);
  22763     const operand_ty = sema.typeOf(operand);
  22764     const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
  22765     const bits = scalar_ty.intInfo(zcu).bits;
  22766     if (bits % 8 != 0) {
  22767         return sema.fail(
  22768             block,
  22769             operand_src,
  22770             "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {d} bits",
  22771             .{ scalar_ty.fmt(pt), bits },
  22772         );
  22773     }
  22774 
  22775     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22776         return Air.internedToRef(val.toIntern());
  22777     }
  22778 
  22779     switch (operand_ty.zigTypeTag(zcu)) {
  22780         .int => {
  22781             const runtime_src = if (try sema.resolveValue(operand)) |val| {
  22782                 if (val.isUndef(zcu)) return pt.undefRef(operand_ty);
  22783                 const result_val = try val.byteSwap(operand_ty, pt, sema.arena);
  22784                 return Air.internedToRef(result_val.toIntern());
  22785             } else operand_src;
  22786 
  22787             try sema.requireRuntimeBlock(block, src, runtime_src);
  22788             return block.addTyOp(.byte_swap, operand_ty, operand);
  22789         },
  22790         .vector => {
  22791             const runtime_src = if (try sema.resolveValue(operand)) |val| {
  22792                 if (val.isUndef(zcu))
  22793                     return pt.undefRef(operand_ty);
  22794 
  22795                 const vec_len = operand_ty.vectorLen(zcu);
  22796                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  22797                 for (elems, 0..) |*elem, i| {
  22798                     const elem_val = try val.elemValue(pt, i);
  22799                     elem.* = (try elem_val.byteSwap(scalar_ty, pt, sema.arena)).toIntern();
  22800                 }
  22801                 return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  22802                     .ty = operand_ty.toIntern(),
  22803                     .storage = .{ .elems = elems },
  22804                 } })));
  22805             } else operand_src;
  22806 
  22807             try sema.requireRuntimeBlock(block, src, runtime_src);
  22808             return block.addTyOp(.byte_swap, operand_ty, operand);
  22809         },
  22810         else => unreachable,
  22811     }
  22812 }
  22813 
  22814 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22815     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  22816     const src = block.nodeOffset(inst_data.src_node);
  22817     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22818     const operand = try sema.resolveInst(inst_data.operand);
  22819     const operand_ty = sema.typeOf(operand);
  22820     const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
  22821 
  22822     if (try sema.typeHasOnePossibleValue(operand_ty)) |val| {
  22823         return Air.internedToRef(val.toIntern());
  22824     }
  22825 
  22826     const pt = sema.pt;
  22827     const zcu = pt.zcu;
  22828     switch (operand_ty.zigTypeTag(zcu)) {
  22829         .int => {
  22830             const runtime_src = if (try sema.resolveValue(operand)) |val| {
  22831                 if (val.isUndef(zcu)) return pt.undefRef(operand_ty);
  22832                 const result_val = try val.bitReverse(operand_ty, pt, sema.arena);
  22833                 return Air.internedToRef(result_val.toIntern());
  22834             } else operand_src;
  22835 
  22836             try sema.requireRuntimeBlock(block, src, runtime_src);
  22837             return block.addTyOp(.bit_reverse, operand_ty, operand);
  22838         },
  22839         .vector => {
  22840             const runtime_src = if (try sema.resolveValue(operand)) |val| {
  22841                 if (val.isUndef(zcu))
  22842                     return pt.undefRef(operand_ty);
  22843 
  22844                 const vec_len = operand_ty.vectorLen(zcu);
  22845                 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
  22846                 for (elems, 0..) |*elem, i| {
  22847                     const elem_val = try val.elemValue(pt, i);
  22848                     elem.* = (try elem_val.bitReverse(scalar_ty, pt, sema.arena)).toIntern();
  22849                 }
  22850                 return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  22851                     .ty = operand_ty.toIntern(),
  22852                     .storage = .{ .elems = elems },
  22853                 } })));
  22854             } else operand_src;
  22855 
  22856             try sema.requireRuntimeBlock(block, src, runtime_src);
  22857             return block.addTyOp(.bit_reverse, operand_ty, operand);
  22858         },
  22859         else => unreachable,
  22860     }
  22861 }
  22862 
  22863 fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22864     const offset = try sema.bitOffsetOf(block, inst);
  22865     return sema.pt.intRef(.comptime_int, offset);
  22866 }
  22867 
  22868 fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  22869     const offset = try sema.bitOffsetOf(block, inst);
  22870     // TODO reminder to make this a compile error for packed structs
  22871     return sema.pt.intRef(.comptime_int, offset / 8);
  22872 }
  22873 
  22874 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
  22875     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  22876     const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
  22877     const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  22878     const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  22879     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  22880 
  22881     const ty = try sema.resolveType(block, ty_src, extra.lhs);
  22882     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name });
  22883 
  22884     const pt = sema.pt;
  22885     const zcu = pt.zcu;
  22886     const ip = &zcu.intern_pool;
  22887     try ty.resolveLayout(pt);
  22888     switch (ty.zigTypeTag(zcu)) {
  22889         .@"struct" => {},
  22890         else => return sema.fail(block, ty_src, "expected struct type, found '{f}'", .{ty.fmt(pt)}),
  22891     }
  22892 
  22893     const field_index = if (ty.isTuple(zcu)) blk: {
  22894         if (field_name.eqlSlice("len", ip)) {
  22895             return sema.fail(block, src, "no offset available for 'len' field of tuple", .{});
  22896         }
  22897         break :blk try sema.tupleFieldIndex(block, ty, field_name, field_name_src);
  22898     } else try sema.structFieldIndex(block, ty, field_name, field_name_src);
  22899 
  22900     if (ty.structFieldIsComptime(field_index, zcu)) {
  22901         return sema.fail(block, src, "no offset available for comptime field", .{});
  22902     }
  22903 
  22904     switch (ty.containerLayout(zcu)) {
  22905         .@"packed" => {
  22906             var bit_sum: u64 = 0;
  22907             const struct_type = ip.loadStructType(ty.toIntern());
  22908             for (0..struct_type.field_types.len) |i| {
  22909                 if (i == field_index) {
  22910                     return bit_sum;
  22911                 }
  22912                 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  22913                 bit_sum += field_ty.bitSize(zcu);
  22914             } else unreachable;
  22915         },
  22916         else => return ty.structFieldOffset(field_index, zcu) * 8,
  22917     }
  22918 }
  22919 
  22920 fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
  22921     const pt = sema.pt;
  22922     const zcu = pt.zcu;
  22923     switch (ty.zigTypeTag(zcu)) {
  22924         .@"struct", .@"enum", .@"union", .@"opaque" => return,
  22925         else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{f}'", .{ty.fmt(pt)}),
  22926     }
  22927 }
  22928 
  22929 /// Returns `true` if the type was a comptime_int.
  22930 fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
  22931     const pt = sema.pt;
  22932     const zcu = pt.zcu;
  22933     switch (ty.zigTypeTag(zcu)) {
  22934         .comptime_int => return true,
  22935         .int => return false,
  22936         else => return sema.fail(block, src, "expected integer type, found '{f}'", .{ty.fmt(pt)}),
  22937     }
  22938 }
  22939 
  22940 fn checkInvalidPtrIntArithmetic(
  22941     sema: *Sema,
  22942     block: *Block,
  22943     src: LazySrcLoc,
  22944     ty: Type,
  22945 ) CompileError!void {
  22946     const pt = sema.pt;
  22947     const zcu = pt.zcu;
  22948     switch (ty.zigTypeTag(zcu)) {
  22949         .pointer => switch (ty.ptrSize(zcu)) {
  22950             .one, .slice => return,
  22951             .many, .c => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
  22952         },
  22953         else => return,
  22954     }
  22955 }
  22956 
  22957 fn checkArithmeticOp(
  22958     sema: *Sema,
  22959     block: *Block,
  22960     src: LazySrcLoc,
  22961     scalar_tag: std.builtin.TypeId,
  22962     lhs_zig_ty_tag: std.builtin.TypeId,
  22963     rhs_zig_ty_tag: std.builtin.TypeId,
  22964     zir_tag: Zir.Inst.Tag,
  22965 ) CompileError!void {
  22966     const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
  22967     const is_float = scalar_tag == .float or scalar_tag == .comptime_float;
  22968 
  22969     if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
  22970         return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
  22971             @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
  22972         });
  22973     }
  22974 }
  22975 
  22976 fn checkPtrOperand(
  22977     sema: *Sema,
  22978     block: *Block,
  22979     ty_src: LazySrcLoc,
  22980     ty: Type,
  22981 ) CompileError!void {
  22982     const pt = sema.pt;
  22983     const zcu = pt.zcu;
  22984     switch (ty.zigTypeTag(zcu)) {
  22985         .pointer => return,
  22986         .@"fn" => {
  22987             const msg = msg: {
  22988                 const msg = try sema.errMsg(
  22989                     ty_src,
  22990                     "expected pointer, found '{f}'",
  22991                     .{ty.fmt(pt)},
  22992                 );
  22993                 errdefer msg.destroy(sema.gpa);
  22994 
  22995                 try sema.errNote(ty_src, msg, "use '&' to obtain a function pointer", .{});
  22996 
  22997                 break :msg msg;
  22998             };
  22999             return sema.failWithOwnedErrorMsg(block, msg);
  23000         },
  23001         .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return,
  23002         else => {},
  23003     }
  23004     return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)});
  23005 }
  23006 
  23007 fn checkPtrType(
  23008     sema: *Sema,
  23009     block: *Block,
  23010     ty_src: LazySrcLoc,
  23011     ty: Type,
  23012     allow_slice: bool,
  23013 ) CompileError!void {
  23014     const pt = sema.pt;
  23015     const zcu = pt.zcu;
  23016     switch (ty.zigTypeTag(zcu)) {
  23017         .pointer => if (allow_slice or !ty.isSlice(zcu)) return,
  23018         .@"fn" => {
  23019             const msg = msg: {
  23020                 const msg = try sema.errMsg(
  23021                     ty_src,
  23022                     "expected pointer type, found '{f}'",
  23023                     .{ty.fmt(pt)},
  23024                 );
  23025                 errdefer msg.destroy(sema.gpa);
  23026 
  23027                 try sema.errNote(ty_src, msg, "use '*const ' to make a function pointer type", .{});
  23028 
  23029                 break :msg msg;
  23030             };
  23031             return sema.failWithOwnedErrorMsg(block, msg);
  23032         },
  23033         .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return,
  23034         else => {},
  23035     }
  23036     return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)});
  23037 }
  23038 
  23039 fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  23040     const pt = sema.pt;
  23041     const zcu = pt.zcu;
  23042     if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) {
  23043         const target = zcu.getTarget();
  23044         const as = ty.ptrAddressSpace(zcu);
  23045         if (target_util.arePointersLogical(target, as)) {
  23046             return sema.failWithOwnedErrorMsg(block, msg: {
  23047                 const msg = try sema.errMsg(src, "illegal operation on logical pointer of type '{f}'", .{ty.fmt(pt)});
  23048                 errdefer msg.destroy(sema.gpa);
  23049                 try sema.errNote(
  23050                     src,
  23051                     msg,
  23052                     "cannot perform arithmetic on pointers with address space '{s}' on target {s}-{s}",
  23053                     .{
  23054                         @tagName(as),
  23055                         @tagName(target.cpu.arch.family()),
  23056                         @tagName(target.os.tag),
  23057                     },
  23058                 );
  23059                 break :msg msg;
  23060             });
  23061         }
  23062     }
  23063 }
  23064 
  23065 fn checkVectorElemType(
  23066     sema: *Sema,
  23067     block: *Block,
  23068     ty_src: LazySrcLoc,
  23069     ty: Type,
  23070 ) CompileError!void {
  23071     const pt = sema.pt;
  23072     const zcu = pt.zcu;
  23073     switch (ty.zigTypeTag(zcu)) {
  23074         .int, .float, .bool => return,
  23075         .optional, .pointer => if (ty.isPtrAtRuntime(zcu)) return,
  23076         else => {},
  23077     }
  23078     return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{f}'", .{ty.fmt(pt)});
  23079 }
  23080 
  23081 fn checkFloatType(
  23082     sema: *Sema,
  23083     block: *Block,
  23084     ty_src: LazySrcLoc,
  23085     ty: Type,
  23086 ) CompileError!void {
  23087     const pt = sema.pt;
  23088     const zcu = pt.zcu;
  23089     switch (ty.zigTypeTag(zcu)) {
  23090         .comptime_int, .comptime_float, .float => {},
  23091         else => return sema.fail(block, ty_src, "expected float type, found '{f}'", .{ty.fmt(pt)}),
  23092     }
  23093 }
  23094 
  23095 fn checkNumericType(
  23096     sema: *Sema,
  23097     block: *Block,
  23098     ty_src: LazySrcLoc,
  23099     ty: Type,
  23100 ) CompileError!void {
  23101     const pt = sema.pt;
  23102     const zcu = pt.zcu;
  23103     switch (ty.zigTypeTag(zcu)) {
  23104         .comptime_float, .float, .comptime_int, .int => {},
  23105         .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) {
  23106             .comptime_float, .float, .comptime_int, .int => {},
  23107             else => |t| return sema.fail(block, ty_src, "expected number, found '{t}'", .{t}),
  23108         },
  23109         else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}),
  23110     }
  23111 }
  23112 
  23113 /// Returns the casted pointer.
  23114 fn checkAtomicPtrOperand(
  23115     sema: *Sema,
  23116     block: *Block,
  23117     elem_ty: Type,
  23118     elem_ty_src: LazySrcLoc,
  23119     ptr: Air.Inst.Ref,
  23120     ptr_src: LazySrcLoc,
  23121     ptr_const: bool,
  23122 ) CompileError!Air.Inst.Ref {
  23123     const pt = sema.pt;
  23124     const zcu = pt.zcu;
  23125     var diag: Zcu.AtomicPtrAlignmentDiagnostics = .{};
  23126     const alignment = zcu.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) {
  23127         error.OutOfMemory => return error.OutOfMemory,
  23128         error.FloatTooBig => return sema.fail(
  23129             block,
  23130             elem_ty_src,
  23131             "expected {d}-bit float type or smaller; found {d}-bit float type",
  23132             .{ diag.max_bits, diag.bits },
  23133         ),
  23134         error.IntTooBig => return sema.fail(
  23135             block,
  23136             elem_ty_src,
  23137             "expected {d}-bit integer type or smaller; found {d}-bit integer type",
  23138             .{ diag.max_bits, diag.bits },
  23139         ),
  23140         error.BadType => return sema.fail(
  23141             block,
  23142             elem_ty_src,
  23143             "expected bool, integer, float, enum, packed struct, or pointer type; found '{f}'",
  23144             .{elem_ty.fmt(pt)},
  23145         ),
  23146     };
  23147 
  23148     var wanted_ptr_data: InternPool.Key.PtrType = .{
  23149         .child = elem_ty.toIntern(),
  23150         .flags = .{
  23151             .alignment = alignment,
  23152             .is_const = ptr_const,
  23153         },
  23154     };
  23155 
  23156     const ptr_ty = sema.typeOf(ptr);
  23157     const ptr_data = switch (ptr_ty.zigTypeTag(zcu)) {
  23158         .pointer => ptr_ty.ptrInfo(zcu),
  23159         else => {
  23160             const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data);
  23161             _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  23162             unreachable;
  23163         },
  23164     };
  23165 
  23166     wanted_ptr_data.flags.address_space = ptr_data.flags.address_space;
  23167     wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero;
  23168     wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile;
  23169 
  23170     const wanted_ptr_ty = try pt.ptrTypeSema(wanted_ptr_data);
  23171     const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
  23172 
  23173     return casted_ptr;
  23174 }
  23175 
  23176 fn checkPtrIsNotComptimeMutable(
  23177     sema: *Sema,
  23178     block: *Block,
  23179     ptr_val: Value,
  23180     ptr_src: LazySrcLoc,
  23181     operand_src: LazySrcLoc,
  23182 ) CompileError!void {
  23183     _ = operand_src;
  23184     if (sema.isComptimeMutablePtr(ptr_val)) {
  23185         return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{});
  23186     }
  23187 }
  23188 
  23189 fn checkIntOrVector(
  23190     sema: *Sema,
  23191     block: *Block,
  23192     operand: Air.Inst.Ref,
  23193     operand_src: LazySrcLoc,
  23194 ) CompileError!Type {
  23195     const pt = sema.pt;
  23196     const zcu = pt.zcu;
  23197     const operand_ty = sema.typeOf(operand);
  23198     switch (operand_ty.zigTypeTag(zcu)) {
  23199         .int => return operand_ty,
  23200         .vector => {
  23201             const elem_ty = operand_ty.childType(zcu);
  23202             switch (elem_ty.zigTypeTag(zcu)) {
  23203                 .int => return elem_ty,
  23204                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{
  23205                     elem_ty.fmt(pt),
  23206                 }),
  23207             }
  23208         },
  23209         else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{
  23210             operand_ty.fmt(pt),
  23211         }),
  23212     }
  23213 }
  23214 
  23215 fn checkIntOrVectorAllowComptime(
  23216     sema: *Sema,
  23217     block: *Block,
  23218     operand_ty: Type,
  23219     operand_src: LazySrcLoc,
  23220 ) CompileError!Type {
  23221     const pt = sema.pt;
  23222     const zcu = pt.zcu;
  23223     switch (operand_ty.zigTypeTag(zcu)) {
  23224         .int, .comptime_int => return operand_ty,
  23225         .vector => {
  23226             const elem_ty = operand_ty.childType(zcu);
  23227             switch (elem_ty.zigTypeTag(zcu)) {
  23228                 .int, .comptime_int => return elem_ty,
  23229                 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{
  23230                     elem_ty.fmt(pt),
  23231                 }),
  23232             }
  23233         },
  23234         else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{
  23235             operand_ty.fmt(pt),
  23236         }),
  23237     }
  23238 }
  23239 
  23240 const SimdBinOp = struct {
  23241     len: ?usize,
  23242     /// Coerced to `result_ty`.
  23243     lhs: Air.Inst.Ref,
  23244     /// Coerced to `result_ty`.
  23245     rhs: Air.Inst.Ref,
  23246     lhs_val: ?Value,
  23247     rhs_val: ?Value,
  23248     /// Only different than `scalar_ty` when it is a vector operation.
  23249     result_ty: Type,
  23250     scalar_ty: Type,
  23251 };
  23252 
  23253 fn checkSimdBinOp(
  23254     sema: *Sema,
  23255     block: *Block,
  23256     src: LazySrcLoc,
  23257     uncasted_lhs: Air.Inst.Ref,
  23258     uncasted_rhs: Air.Inst.Ref,
  23259     lhs_src: LazySrcLoc,
  23260     rhs_src: LazySrcLoc,
  23261 ) CompileError!SimdBinOp {
  23262     const pt = sema.pt;
  23263     const zcu = pt.zcu;
  23264     const lhs_ty = sema.typeOf(uncasted_lhs);
  23265     const rhs_ty = sema.typeOf(uncasted_rhs);
  23266 
  23267     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  23268     const vec_len: ?usize = if (lhs_ty.zigTypeTag(zcu) == .vector) lhs_ty.vectorLen(zcu) else null;
  23269     const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{
  23270         .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
  23271     });
  23272     const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src);
  23273     const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src);
  23274 
  23275     return SimdBinOp{
  23276         .len = vec_len,
  23277         .lhs = lhs,
  23278         .rhs = rhs,
  23279         .lhs_val = try sema.resolveValue(lhs),
  23280         .rhs_val = try sema.resolveValue(rhs),
  23281         .result_ty = result_ty,
  23282         .scalar_ty = result_ty.scalarType(zcu),
  23283     };
  23284 }
  23285 
  23286 fn checkVectorizableBinaryOperands(
  23287     sema: *Sema,
  23288     block: *Block,
  23289     src: LazySrcLoc,
  23290     lhs_ty: Type,
  23291     rhs_ty: Type,
  23292     lhs_src: LazySrcLoc,
  23293     rhs_src: LazySrcLoc,
  23294 ) CompileError!void {
  23295     const pt = sema.pt;
  23296     const zcu = pt.zcu;
  23297     const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
  23298     const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
  23299     if (lhs_zig_ty_tag != .vector and rhs_zig_ty_tag != .vector) return;
  23300 
  23301     const lhs_is_vector = switch (lhs_zig_ty_tag) {
  23302         .vector, .array => true,
  23303         else => false,
  23304     };
  23305     const rhs_is_vector = switch (rhs_zig_ty_tag) {
  23306         .vector, .array => true,
  23307         else => false,
  23308     };
  23309 
  23310     if (lhs_is_vector and rhs_is_vector) {
  23311         const lhs_len = lhs_ty.arrayLen(zcu);
  23312         const rhs_len = rhs_ty.arrayLen(zcu);
  23313         if (lhs_len != rhs_len) {
  23314             const msg = msg: {
  23315                 const msg = try sema.errMsg(src, "vector length mismatch", .{});
  23316                 errdefer msg.destroy(sema.gpa);
  23317                 try sema.errNote(lhs_src, msg, "length {d} here", .{lhs_len});
  23318                 try sema.errNote(rhs_src, msg, "length {d} here", .{rhs_len});
  23319                 break :msg msg;
  23320             };
  23321             return sema.failWithOwnedErrorMsg(block, msg);
  23322         }
  23323     } else {
  23324         const msg = msg: {
  23325             const msg = try sema.errMsg(src, "mixed scalar and vector operands: '{f}' and '{f}'", .{
  23326                 lhs_ty.fmt(pt), rhs_ty.fmt(pt),
  23327             });
  23328             errdefer msg.destroy(sema.gpa);
  23329             if (lhs_is_vector) {
  23330                 try sema.errNote(lhs_src, msg, "vector here", .{});
  23331                 try sema.errNote(rhs_src, msg, "scalar here", .{});
  23332             } else {
  23333                 try sema.errNote(lhs_src, msg, "scalar here", .{});
  23334                 try sema.errNote(rhs_src, msg, "vector here", .{});
  23335             }
  23336             break :msg msg;
  23337         };
  23338         return sema.failWithOwnedErrorMsg(block, msg);
  23339     }
  23340 }
  23341 
  23342 fn resolveExportOptions(
  23343     sema: *Sema,
  23344     block: *Block,
  23345     src: LazySrcLoc,
  23346     zir_ref: Zir.Inst.Ref,
  23347 ) CompileError!Zcu.Export.Options {
  23348     const pt = sema.pt;
  23349     const zcu = pt.zcu;
  23350     const gpa = sema.gpa;
  23351     const ip = &zcu.intern_pool;
  23352     const export_options_ty = try sema.getBuiltinType(src, .ExportOptions);
  23353     const air_ref = try sema.resolveInst(zir_ref);
  23354     const options = try sema.coerce(block, export_options_ty, air_ref, src);
  23355 
  23356     const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  23357     const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  23358     const section_src = block.src(.{ .init_field_section = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  23359     const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  23360 
  23361     const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
  23362     const name = try sema.toConstString(block, name_src, name_operand, .{ .simple = .export_options });
  23363 
  23364     const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
  23365     const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ .simple = .export_options });
  23366     const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage);
  23367 
  23368     const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "section", .no_embedded_nulls), section_src);
  23369     const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ .simple = .export_options });
  23370     const section = if (section_opt_val.optionalValue(zcu)) |section_val|
  23371         try sema.toConstString(block, section_src, Air.internedToRef(section_val.toIntern()), .{ .simple = .export_options })
  23372     else
  23373         null;
  23374 
  23375     const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
  23376     const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{ .simple = .export_options });
  23377     const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility);
  23378 
  23379     if (name.len < 1) {
  23380         return sema.fail(block, name_src, "exported symbol name cannot be empty", .{});
  23381     }
  23382 
  23383     if (visibility != .default and linkage == .internal) {
  23384         return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{
  23385             name, @tagName(visibility),
  23386         });
  23387     }
  23388 
  23389     return .{
  23390         .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls),
  23391         .linkage = linkage,
  23392         .section = try ip.getOrPutStringOpt(gpa, pt.tid, section, .no_embedded_nulls),
  23393         .visibility = visibility,
  23394     };
  23395 }
  23396 
  23397 fn resolveBuiltinEnum(
  23398     sema: *Sema,
  23399     block: *Block,
  23400     src: LazySrcLoc,
  23401     zir_ref: Zir.Inst.Ref,
  23402     comptime name: Zcu.BuiltinDecl,
  23403     reason: ComptimeReason,
  23404 ) CompileError!@field(std.builtin, @tagName(name)) {
  23405     const ty = try sema.getBuiltinType(src, name);
  23406     const air_ref = try sema.resolveInst(zir_ref);
  23407     const coerced = try sema.coerce(block, ty, air_ref, src);
  23408     const val = try sema.resolveConstDefinedValue(block, src, coerced, reason);
  23409     return sema.interpretBuiltinType(block, src, val, @field(std.builtin, @tagName(name)));
  23410 }
  23411 
  23412 fn resolveAtomicOrder(
  23413     sema: *Sema,
  23414     block: *Block,
  23415     src: LazySrcLoc,
  23416     zir_ref: Zir.Inst.Ref,
  23417     reason: ComptimeReason,
  23418 ) CompileError!std.builtin.AtomicOrder {
  23419     return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicOrder, reason);
  23420 }
  23421 
  23422 fn resolveAtomicRmwOp(
  23423     sema: *Sema,
  23424     block: *Block,
  23425     src: LazySrcLoc,
  23426     zir_ref: Zir.Inst.Ref,
  23427 ) CompileError!std.builtin.AtomicRmwOp {
  23428     return sema.resolveBuiltinEnum(block, src, zir_ref, .AtomicRmwOp, .{ .simple = .operand_atomicRmw_operation });
  23429 }
  23430 
  23431 fn zirCmpxchg(
  23432     sema: *Sema,
  23433     block: *Block,
  23434     extended: Zir.Inst.Extended.InstData,
  23435 ) CompileError!Air.Inst.Ref {
  23436     const pt = sema.pt;
  23437     const zcu = pt.zcu;
  23438     const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data;
  23439     const air_tag: Air.Inst.Tag = switch (extended.small) {
  23440         0 => .cmpxchg_weak,
  23441         1 => .cmpxchg_strong,
  23442         else => unreachable,
  23443     };
  23444     const src = block.nodeOffset(extra.node);
  23445     // zig fmt: off
  23446     const elem_ty_src       = block.builtinCallArgSrc(extra.node, 0);
  23447     const ptr_src           = block.builtinCallArgSrc(extra.node, 1);
  23448     const expected_src      = block.builtinCallArgSrc(extra.node, 2);
  23449     const new_value_src     = block.builtinCallArgSrc(extra.node, 3);
  23450     const success_order_src = block.builtinCallArgSrc(extra.node, 4);
  23451     const failure_order_src = block.builtinCallArgSrc(extra.node, 5);
  23452     // zig fmt: on
  23453     const expected_value = try sema.resolveInst(extra.expected_value);
  23454     const elem_ty = sema.typeOf(expected_value);
  23455     if (elem_ty.zigTypeTag(zcu) == .float) {
  23456         return sema.fail(
  23457             block,
  23458             elem_ty_src,
  23459             "expected bool, integer, enum, packed struct, or pointer type; found '{f}'",
  23460             .{elem_ty.fmt(pt)},
  23461         );
  23462     }
  23463     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  23464     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  23465     const new_value = try sema.coerce(block, elem_ty, try sema.resolveInst(extra.new_value), new_value_src);
  23466     const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, .{ .simple = .atomic_order });
  23467     const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, .{ .simple = .atomic_order });
  23468 
  23469     if (@intFromEnum(success_order) < @intFromEnum(std.builtin.AtomicOrder.monotonic)) {
  23470         return sema.fail(block, success_order_src, "success atomic ordering must be monotonic or stricter", .{});
  23471     }
  23472     if (@intFromEnum(failure_order) < @intFromEnum(std.builtin.AtomicOrder.monotonic)) {
  23473         return sema.fail(block, failure_order_src, "failure atomic ordering must be monotonic or stricter", .{});
  23474     }
  23475     if (@intFromEnum(failure_order) > @intFromEnum(success_order)) {
  23476         return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{});
  23477     }
  23478     if (failure_order == .release or failure_order == .acq_rel) {
  23479         return sema.fail(block, failure_order_src, "failure atomic ordering must not be release or acq_rel", .{});
  23480     }
  23481 
  23482     const result_ty = try pt.optionalType(elem_ty.toIntern());
  23483 
  23484     // special case zero bit types
  23485     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
  23486         return Air.internedToRef((try pt.intern(.{ .opt = .{
  23487             .ty = result_ty.toIntern(),
  23488             .val = .none,
  23489         } })));
  23490     }
  23491 
  23492     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  23493         if (try sema.resolveValue(expected_value)) |expected_val| {
  23494             if (try sema.resolveValue(new_value)) |new_val| {
  23495                 if (expected_val.isUndef(zcu) or new_val.isUndef(zcu)) {
  23496                     // TODO: this should probably cause the memory stored at the pointer
  23497                     // to become undef as well
  23498                     return pt.undefRef(result_ty);
  23499                 }
  23500                 const ptr_ty = sema.typeOf(ptr);
  23501                 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  23502                 const result_val = try pt.intern(.{ .opt = .{
  23503                     .ty = result_ty.toIntern(),
  23504                     .val = if (stored_val.eql(expected_val, elem_ty, zcu)) blk: {
  23505                         try sema.storePtr(block, src, ptr, new_value);
  23506                         break :blk .none;
  23507                     } else stored_val.toIntern(),
  23508                 } });
  23509                 return Air.internedToRef(result_val);
  23510             } else break :rs new_value_src;
  23511         } else break :rs expected_src;
  23512     } else ptr_src;
  23513 
  23514     const flags: u32 = @as(u32, @intFromEnum(success_order)) |
  23515         (@as(u32, @intFromEnum(failure_order)) << 3);
  23516 
  23517     try sema.requireRuntimeBlock(block, src, runtime_src);
  23518     return block.addInst(.{
  23519         .tag = air_tag,
  23520         .data = .{ .ty_pl = .{
  23521             .ty = Air.internedToRef(result_ty.toIntern()),
  23522             .payload = try sema.addExtra(Air.Cmpxchg{
  23523                 .ptr = ptr,
  23524                 .expected_value = expected_value,
  23525                 .new_value = new_value,
  23526                 .flags = flags,
  23527             }),
  23528         } },
  23529     });
  23530 }
  23531 
  23532 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23533     const pt = sema.pt;
  23534     const zcu = pt.zcu;
  23535     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23536     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23537     const src = block.nodeOffset(inst_data.src_node);
  23538     const scalar_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  23539     const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat");
  23540 
  23541     switch (dest_ty.zigTypeTag(zcu)) {
  23542         .array, .vector => {},
  23543         else => return sema.fail(block, src, "expected array or vector type, found '{f}'", .{dest_ty.fmt(pt)}),
  23544     }
  23545 
  23546     const operand = try sema.resolveInst(extra.rhs);
  23547     const scalar_ty = dest_ty.childType(zcu);
  23548     const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src);
  23549 
  23550     const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu));
  23551 
  23552     if (try sema.typeHasOnePossibleValue(dest_ty)) |val| {
  23553         return Air.internedToRef(val.toIntern());
  23554     }
  23555 
  23556     // We also need this case because `[0:s]T` is not OPV.
  23557     if (len == 0) {
  23558         const empty_aggregate = try pt.intern(.{ .aggregate = .{
  23559             .ty = dest_ty.toIntern(),
  23560             .storage = .{ .elems = &.{} },
  23561         } });
  23562         return Air.internedToRef(empty_aggregate);
  23563     }
  23564 
  23565     const maybe_sentinel = dest_ty.sentinel(zcu);
  23566 
  23567     if (try sema.resolveValue(scalar)) |scalar_val| {
  23568         if (scalar_val.isUndef(zcu) and maybe_sentinel == null) {
  23569             return pt.undefRef(dest_ty);
  23570         }
  23571         // TODO: I didn't want to put `.aggregate` on a separate line here; `zig fmt` bugs have forced my hand
  23572         return Air.internedToRef(try pt.intern(.{
  23573             .aggregate = .{
  23574                 .ty = dest_ty.toIntern(),
  23575                 .storage = s: {
  23576                     full: {
  23577                         if (dest_ty.zigTypeTag(zcu) == .vector) break :full;
  23578                         const sentinel = maybe_sentinel orelse break :full;
  23579                         if (sentinel.toIntern() == scalar_val.toIntern()) break :full;
  23580                         // This is a array with non-zero length and a sentinel which does not match the element.
  23581                         // We have to use the full `elems` representation.
  23582                         const elems = try sema.arena.alloc(InternPool.Index, len + 1);
  23583                         @memset(elems[0..len], scalar_val.toIntern());
  23584                         elems[len] = sentinel.toIntern();
  23585                         break :s .{ .elems = elems };
  23586                     }
  23587                     break :s .{ .repeated_elem = scalar_val.toIntern() };
  23588                 },
  23589             },
  23590         }));
  23591     }
  23592 
  23593     try sema.requireRuntimeBlock(block, src, scalar_src);
  23594 
  23595     switch (dest_ty.zigTypeTag(zcu)) {
  23596         .array => {
  23597             const elems = try sema.arena.alloc(Air.Inst.Ref, len + @intFromBool(maybe_sentinel != null));
  23598             @memset(elems[0..len], scalar);
  23599             if (maybe_sentinel) |s| elems[len] = Air.internedToRef(s.toIntern());
  23600             return block.addAggregateInit(dest_ty, elems);
  23601         },
  23602         .vector => return block.addTyOp(.splat, dest_ty, scalar),
  23603         else => unreachable,
  23604     }
  23605 }
  23606 
  23607 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23608     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23609     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  23610     const op_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  23611     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  23612     const operation = try sema.resolveBuiltinEnum(block, op_src, extra.lhs, .ReduceOp, .{ .simple = .operand_reduce_operation });
  23613     const operand = try sema.resolveInst(extra.rhs);
  23614     const operand_ty = sema.typeOf(operand);
  23615     const pt = sema.pt;
  23616     const zcu = pt.zcu;
  23617 
  23618     if (operand_ty.zigTypeTag(zcu) != .vector) {
  23619         return sema.fail(block, operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)});
  23620     }
  23621 
  23622     const scalar_ty = operand_ty.childType(zcu);
  23623 
  23624     // Type-check depending on operation.
  23625     switch (operation) {
  23626         .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) {
  23627             .int, .bool => {},
  23628             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{f}'", .{
  23629                 @tagName(operation), operand_ty.fmt(pt),
  23630             }),
  23631         },
  23632         .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(zcu)) {
  23633             .int, .float => {},
  23634             else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{f}'", .{
  23635                 @tagName(operation), operand_ty.fmt(pt),
  23636             }),
  23637         },
  23638     }
  23639 
  23640     const vec_len = operand_ty.vectorLen(zcu);
  23641     if (vec_len == 0) {
  23642         // TODO re-evaluate if we should introduce a "neutral value" for some operations,
  23643         // e.g. zero for add and one for mul.
  23644         return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{});
  23645     }
  23646 
  23647     if (try sema.resolveValue(operand)) |operand_val| {
  23648         if (operand_val.isUndef(zcu)) return pt.undefRef(scalar_ty);
  23649 
  23650         var accum: Value = try operand_val.elemValue(pt, 0);
  23651         var i: u32 = 1;
  23652         while (i < vec_len) : (i += 1) {
  23653             const elem_val = try operand_val.elemValue(pt, i);
  23654             switch (operation) {
  23655                 .And => accum = try accum.bitwiseAnd(elem_val, scalar_ty, sema.arena, pt),
  23656                 .Or => accum = try accum.bitwiseOr(elem_val, scalar_ty, sema.arena, pt),
  23657                 .Xor => accum = try accum.bitwiseXor(elem_val, scalar_ty, sema.arena, pt),
  23658                 .Min => accum = accum.numberMin(elem_val, zcu),
  23659                 .Max => accum = accum.numberMax(elem_val, zcu),
  23660                 .Add => accum = try arith.addMaybeWrap(sema, scalar_ty, accum, elem_val),
  23661                 .Mul => accum = try arith.mulMaybeWrap(sema, scalar_ty, accum, elem_val),
  23662             }
  23663         }
  23664         return Air.internedToRef(accum.toIntern());
  23665     }
  23666 
  23667     try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src);
  23668     return block.addReduce(operand, operation);
  23669 }
  23670 
  23671 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23672     const pt = sema.pt;
  23673     const zcu = pt.zcu;
  23674     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23675     const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
  23676     const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  23677     const mask_src = block.builtinCallArgSrc(inst_data.src_node, 3);
  23678 
  23679     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  23680     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  23681     const a = try sema.resolveInst(extra.a);
  23682     const b = try sema.resolveInst(extra.b);
  23683     var mask = try sema.resolveInst(extra.mask);
  23684     var mask_ty = sema.typeOf(mask);
  23685 
  23686     const mask_len = switch (sema.typeOf(mask).zigTypeTag(zcu)) {
  23687         .array, .vector => sema.typeOf(mask).arrayLen(zcu),
  23688         else => return sema.fail(block, mask_src, "expected vector or array, found '{f}'", .{sema.typeOf(mask).fmt(pt)}),
  23689     };
  23690     mask_ty = try pt.vectorType(.{
  23691         .len = @intCast(mask_len),
  23692         .child = .i32_type,
  23693     });
  23694     mask = try sema.coerce(block, mask_ty, mask, mask_src);
  23695     const mask_val = try sema.resolveConstValue(block, mask_src, mask, .{ .simple = .operand_shuffle_mask });
  23696     return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(mask_len));
  23697 }
  23698 
  23699 fn analyzeShuffle(
  23700     sema: *Sema,
  23701     block: *Block,
  23702     src_node: std.zig.Ast.Node.Offset,
  23703     elem_ty: Type,
  23704     a_uncoerced: Air.Inst.Ref,
  23705     b_uncoerced: Air.Inst.Ref,
  23706     mask: Value,
  23707     mask_len: u32,
  23708 ) CompileError!Air.Inst.Ref {
  23709     const pt = sema.pt;
  23710     const zcu = pt.zcu;
  23711     const a_src = block.builtinCallArgSrc(src_node, 1);
  23712     const b_src = block.builtinCallArgSrc(src_node, 2);
  23713     const mask_src = block.builtinCallArgSrc(src_node, 3);
  23714 
  23715     // If the type of `a` is `@Type(.undefined)`, i.e. the argument is untyped,
  23716     // this is 0, because it is an error to index into this vector.
  23717     const a_len: u32 = switch (sema.typeOf(a_uncoerced).zigTypeTag(zcu)) {
  23718         .array, .vector => @intCast(sema.typeOf(a_uncoerced).arrayLen(zcu)),
  23719         .undefined => 0,
  23720         else => return sema.fail(block, a_src, "expected vector of '{f}', found '{f}'", .{
  23721             elem_ty.fmt(pt), sema.typeOf(a_uncoerced).fmt(pt),
  23722         }),
  23723     };
  23724     const a_ty = try pt.vectorType(.{ .len = a_len, .child = elem_ty.toIntern() });
  23725     const a_coerced = try sema.coerce(block, a_ty, a_uncoerced, a_src);
  23726 
  23727     // 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.
  23728     const b_len: u32 = switch (sema.typeOf(b_uncoerced).zigTypeTag(zcu)) {
  23729         .array, .vector => @intCast(sema.typeOf(b_uncoerced).arrayLen(zcu)),
  23730         .undefined => 0,
  23731         else => return sema.fail(block, b_src, "expected vector of '{f}', found '{f}'", .{
  23732             elem_ty.fmt(pt), sema.typeOf(b_uncoerced).fmt(pt),
  23733         }),
  23734     };
  23735     const b_ty = try pt.vectorType(.{ .len = b_len, .child = elem_ty.toIntern() });
  23736     const b_coerced = try sema.coerce(block, b_ty, b_uncoerced, b_src);
  23737 
  23738     const result_ty = try pt.vectorType(.{ .len = mask_len, .child = elem_ty.toIntern() });
  23739 
  23740     // We're going to pre-emptively reserve space in `sema.air_extra`. The reason for this is we need
  23741     // a `u32` buffer of length `mask_len` anyway, and putting it in `sema.air_extra` avoids a copy
  23742     // in the runtime case. If the result is comptime-known, we'll shrink `air_extra` back.
  23743     const air_extra_idx: u32 = @intCast(sema.air_extra.items.len);
  23744     const air_mask_buf = try sema.air_extra.addManyAsSlice(sema.gpa, mask_len);
  23745 
  23746     // We want to interpret that buffer in `air_extra` in a few ways. Initially, we'll consider its
  23747     // elements as `Air.Inst.ShuffleTwoMask`, essentially representing the raw mask values; then, we'll
  23748     // convert it to `InternPool.Index` or `Air.Inst.ShuffleOneMask` if there are comptime-known operands.
  23749     const mask_ip_index: []InternPool.Index = @ptrCast(air_mask_buf);
  23750     const mask_shuffle_one: []Air.ShuffleOneMask = @ptrCast(air_mask_buf);
  23751     const mask_shuffle_two: []Air.ShuffleTwoMask = @ptrCast(air_mask_buf);
  23752 
  23753     // Initial loop: check mask elements, populate `mask_shuffle_two`.
  23754     var a_used = false;
  23755     var b_used = false;
  23756     for (mask_shuffle_two, 0..mask_len) |*out, mask_idx| {
  23757         const mask_val = try mask.elemValue(pt, mask_idx);
  23758         if (mask_val.isUndef(zcu)) {
  23759             out.* = .undef;
  23760             continue;
  23761         }
  23762         // Safe because mask elements are `i32` and we already checked for undef:
  23763         const raw = (try sema.resolveLazyValue(mask_val)).toSignedInt(zcu);
  23764         if (raw >= 0) {
  23765             const idx: u32 = @intCast(raw);
  23766             a_used = true;
  23767             out.* = .aElem(idx);
  23768             if (idx >= a_len) return sema.failWithOwnedErrorMsg(block, msg: {
  23769                 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx});
  23770                 errdefer msg.destroy(sema.gpa);
  23771                 try sema.errNote(a_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, a_ty.fmt(pt) });
  23772                 if (idx < b_len) {
  23773                     try sema.errNote(b_src, msg, "use '~@as(u32, {d})' to index into second vector given here", .{idx});
  23774                 }
  23775                 break :msg msg;
  23776             });
  23777         } else {
  23778             const idx: u32 = @intCast(~raw);
  23779             b_used = true;
  23780             out.* = .bElem(idx);
  23781             if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: {
  23782                 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx});
  23783                 errdefer msg.destroy(sema.gpa);
  23784                 try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, b_ty.fmt(pt) });
  23785                 break :msg msg;
  23786             });
  23787         }
  23788     }
  23789 
  23790     const maybe_a_val = try sema.resolveValue(a_coerced);
  23791     const maybe_b_val = try sema.resolveValue(b_coerced);
  23792 
  23793     const a_rt = a_used and maybe_a_val == null;
  23794     const b_rt = b_used and maybe_b_val == null;
  23795 
  23796     if (a_rt and b_rt) {
  23797         // Both operands are needed and runtime-known. We need a `[]ShuffleTwomask`... which is
  23798         // exactly what we already have in `mask_shuffle_two`! So, we're basically done already.
  23799         // We just need to append the two operands.
  23800         try sema.air_extra.ensureUnusedCapacity(sema.gpa, 2);
  23801         sema.appendRefsAssumeCapacity(&.{ a_coerced, b_coerced });
  23802         return block.addInst(.{
  23803             .tag = .shuffle_two,
  23804             .data = .{ .ty_pl = .{
  23805                 .ty = Air.internedToRef(result_ty.toIntern()),
  23806                 .payload = air_extra_idx,
  23807             } },
  23808         });
  23809     } else if (a_rt) {
  23810         // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`.
  23811         for (mask_shuffle_two, mask_shuffle_one) |in, *out| {
  23812             out.* = switch (in.unwrap()) {
  23813                 .undef => .value(try pt.undefValue(elem_ty)),
  23814                 .a_elem => |idx| .elem(idx),
  23815                 .b_elem => |idx| .value(try maybe_b_val.?.elemValue(pt, idx)),
  23816             };
  23817         }
  23818         // Now just append our single runtime operand, and we're done.
  23819         try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1);
  23820         sema.appendRefsAssumeCapacity(&.{a_coerced});
  23821         return block.addInst(.{
  23822             .tag = .shuffle_one,
  23823             .data = .{ .ty_pl = .{
  23824                 .ty = Air.internedToRef(result_ty.toIntern()),
  23825                 .payload = air_extra_idx,
  23826             } },
  23827         });
  23828     } else if (b_rt) {
  23829         // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`.
  23830         for (mask_shuffle_two, mask_shuffle_one) |in, *out| {
  23831             out.* = switch (in.unwrap()) {
  23832                 .undef => .value(try pt.undefValue(elem_ty)),
  23833                 .a_elem => |idx| .value(try maybe_a_val.?.elemValue(pt, idx)),
  23834                 .b_elem => |idx| .elem(idx),
  23835             };
  23836         }
  23837         // Now just append our single runtime operand, and we're done.
  23838         try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1);
  23839         sema.appendRefsAssumeCapacity(&.{b_coerced});
  23840         return block.addInst(.{
  23841             .tag = .shuffle_one,
  23842             .data = .{ .ty_pl = .{
  23843                 .ty = Air.internedToRef(result_ty.toIntern()),
  23844                 .payload = air_extra_idx,
  23845             } },
  23846         });
  23847     } else {
  23848         // The result will be comptime-known. We must convert the `ShuffleTwoMask` values to
  23849         // `InternPool.Index` values using the known operands.
  23850         for (mask_shuffle_two, mask_ip_index) |in, *out| {
  23851             const val: Value = switch (in.unwrap()) {
  23852                 .undef => try pt.undefValue(elem_ty),
  23853                 .a_elem => |idx| try maybe_a_val.?.elemValue(pt, idx),
  23854                 .b_elem => |idx| try maybe_b_val.?.elemValue(pt, idx),
  23855             };
  23856             out.* = val.toIntern();
  23857         }
  23858         const res = try pt.intern(.{ .aggregate = .{
  23859             .ty = result_ty.toIntern(),
  23860             .storage = .{ .elems = mask_ip_index },
  23861         } });
  23862         // We have a comptime-known result, so didn't need `air_mask_buf` -- remove it from `sema.air_extra`.
  23863         assert(sema.air_extra.items.len == air_extra_idx + air_mask_buf.len);
  23864         sema.air_extra.shrinkRetainingCapacity(air_extra_idx);
  23865         return Air.internedToRef(res);
  23866     }
  23867 }
  23868 
  23869 fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  23870     const pt = sema.pt;
  23871     const zcu = pt.zcu;
  23872     const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data;
  23873 
  23874     const src = block.nodeOffset(extra.node);
  23875     const elem_ty_src = block.builtinCallArgSrc(extra.node, 0);
  23876     const pred_src = block.builtinCallArgSrc(extra.node, 1);
  23877     const a_src = block.builtinCallArgSrc(extra.node, 2);
  23878     const b_src = block.builtinCallArgSrc(extra.node, 3);
  23879 
  23880     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  23881     try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
  23882     const pred_uncoerced = try sema.resolveInst(extra.pred);
  23883     const pred_ty = sema.typeOf(pred_uncoerced);
  23884 
  23885     const vec_len_u64 = switch (pred_ty.zigTypeTag(zcu)) {
  23886         .vector, .array => pred_ty.arrayLen(zcu),
  23887         else => return sema.fail(block, pred_src, "expected vector or array, found '{f}'", .{pred_ty.fmt(pt)}),
  23888     };
  23889     const vec_len: u32 = @intCast(try sema.usizeCast(block, pred_src, vec_len_u64));
  23890 
  23891     const bool_vec_ty = try pt.vectorType(.{
  23892         .len = vec_len,
  23893         .child = .bool_type,
  23894     });
  23895     const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src);
  23896 
  23897     const vec_ty = try pt.vectorType(.{
  23898         .len = vec_len,
  23899         .child = elem_ty.toIntern(),
  23900     });
  23901     const a = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.a), a_src);
  23902     const b = try sema.coerce(block, vec_ty, try sema.resolveInst(extra.b), b_src);
  23903 
  23904     const maybe_pred = try sema.resolveValue(pred);
  23905     const maybe_a = try sema.resolveValue(a);
  23906     const maybe_b = try sema.resolveValue(b);
  23907 
  23908     const runtime_src = if (maybe_pred) |pred_val| rs: {
  23909         if (pred_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  23910 
  23911         if (maybe_a) |a_val| {
  23912             if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  23913 
  23914             if (maybe_b) |b_val| {
  23915                 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  23916 
  23917                 const elems = try sema.gpa.alloc(InternPool.Index, vec_len);
  23918                 defer sema.gpa.free(elems);
  23919                 for (elems, 0..) |*elem, i| {
  23920                     const pred_elem_val = try pred_val.elemValue(pt, i);
  23921                     const should_choose_a = pred_elem_val.toBool();
  23922                     elem.* = (try (if (should_choose_a) a_val else b_val).elemValue(pt, i)).toIntern();
  23923                 }
  23924 
  23925                 return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  23926                     .ty = vec_ty.toIntern(),
  23927                     .storage = .{ .elems = elems },
  23928                 } })));
  23929             } else {
  23930                 break :rs b_src;
  23931             }
  23932         } else {
  23933             if (maybe_b) |b_val| {
  23934                 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  23935             }
  23936             break :rs a_src;
  23937         }
  23938     } else rs: {
  23939         if (maybe_a) |a_val| {
  23940             if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  23941         }
  23942         if (maybe_b) |b_val| {
  23943             if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty);
  23944         }
  23945         break :rs pred_src;
  23946     };
  23947 
  23948     try sema.requireRuntimeBlock(block, src, runtime_src);
  23949     return block.addInst(.{
  23950         .tag = .select,
  23951         .data = .{ .pl_op = .{
  23952             .operand = pred,
  23953             .payload = try block.sema.addExtra(Air.Bin{
  23954                 .lhs = a,
  23955                 .rhs = b,
  23956             }),
  23957         } },
  23958     });
  23959 }
  23960 
  23961 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  23962     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  23963     const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
  23964     // zig fmt: off
  23965     const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  23966     const ptr_src     = block.builtinCallArgSrc(inst_data.src_node, 1);
  23967     const order_src   = block.builtinCallArgSrc(inst_data.src_node, 2);
  23968     // zig fmt: on
  23969     const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
  23970     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  23971     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
  23972     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
  23973 
  23974     switch (order) {
  23975         .release, .acq_rel => {
  23976             return sema.fail(
  23977                 block,
  23978                 order_src,
  23979                 "@atomicLoad atomic ordering must not be release or acq_rel",
  23980                 .{},
  23981             );
  23982         },
  23983         else => {},
  23984     }
  23985 
  23986     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  23987         return Air.internedToRef(val.toIntern());
  23988     }
  23989 
  23990     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  23991         if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| {
  23992             return Air.internedToRef(elem_val.toIntern());
  23993         }
  23994     }
  23995 
  23996     try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src);
  23997     return block.addInst(.{
  23998         .tag = .atomic_load,
  23999         .data = .{ .atomic_load = .{
  24000             .ptr = ptr,
  24001             .order = order,
  24002         } },
  24003     });
  24004 }
  24005 
  24006 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  24007     const pt = sema.pt;
  24008     const zcu = pt.zcu;
  24009     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24010     const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
  24011     const src = block.nodeOffset(inst_data.src_node);
  24012     // zig fmt: off
  24013     const elem_ty_src    = block.builtinCallArgSrc(inst_data.src_node, 0);
  24014     const ptr_src        = block.builtinCallArgSrc(inst_data.src_node, 1);
  24015     const op_src         = block.builtinCallArgSrc(inst_data.src_node, 2);
  24016     const operand_src    = block.builtinCallArgSrc(inst_data.src_node, 3);
  24017     const order_src      = block.builtinCallArgSrc(inst_data.src_node, 4);
  24018     // zig fmt: on
  24019     const operand = try sema.resolveInst(extra.operand);
  24020     const elem_ty = sema.typeOf(operand);
  24021     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  24022     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  24023     const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);
  24024 
  24025     switch (elem_ty.zigTypeTag(zcu)) {
  24026         .@"enum" => if (op != .Xchg) {
  24027             return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{});
  24028         },
  24029         .bool => if (op != .Xchg) {
  24030             return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{});
  24031         },
  24032         .float => switch (op) {
  24033             .Xchg, .Add, .Sub, .Max, .Min => {},
  24034             else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}),
  24035         },
  24036         else => {},
  24037     }
  24038     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
  24039 
  24040     if (order == .unordered) {
  24041         return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be unordered", .{});
  24042     }
  24043 
  24044     // special case zero bit types
  24045     if (try sema.typeHasOnePossibleValue(elem_ty)) |val| {
  24046         return Air.internedToRef(val.toIntern());
  24047     }
  24048 
  24049     const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
  24050         const maybe_operand_val = try sema.resolveValue(operand);
  24051         const operand_val = maybe_operand_val orelse {
  24052             try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
  24053             break :rs operand_src;
  24054         };
  24055         if (sema.isComptimeMutablePtr(ptr_val)) {
  24056             const ptr_ty = sema.typeOf(ptr);
  24057             const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
  24058             const new_val = switch (op) {
  24059                 // zig fmt: off
  24060                 .Xchg => operand_val,
  24061                 .Add  => try arith.addMaybeWrap(sema, elem_ty, stored_val, operand_val),
  24062                 .Sub  => try arith.subMaybeWrap(sema, elem_ty, stored_val, operand_val),
  24063                 .And  => try stored_val.bitwiseAnd   (operand_val, elem_ty, sema.arena, pt ),
  24064                 .Nand => try stored_val.bitwiseNand  (operand_val, elem_ty, sema.arena, pt ),
  24065                 .Or   => try stored_val.bitwiseOr    (operand_val, elem_ty, sema.arena, pt ),
  24066                 .Xor  => try stored_val.bitwiseXor   (operand_val, elem_ty, sema.arena, pt ),
  24067                 .Max  =>     stored_val.numberMax    (operand_val,                      zcu),
  24068                 .Min  =>     stored_val.numberMin    (operand_val,                      zcu),
  24069                 // zig fmt: on
  24070             };
  24071             try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty);
  24072             return Air.internedToRef(stored_val.toIntern());
  24073         } else break :rs ptr_src;
  24074     } else ptr_src;
  24075 
  24076     const flags: u32 = @as(u32, @intFromEnum(order)) | (@as(u32, @intFromEnum(op)) << 3);
  24077 
  24078     try sema.requireRuntimeBlock(block, src, runtime_src);
  24079     return block.addInst(.{
  24080         .tag = .atomic_rmw,
  24081         .data = .{ .pl_op = .{
  24082             .operand = ptr,
  24083             .payload = try sema.addExtra(Air.AtomicRmw{
  24084                 .operand = operand,
  24085                 .flags = flags,
  24086             }),
  24087         } },
  24088     });
  24089 }
  24090 
  24091 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  24092     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24093     const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
  24094     const src = block.nodeOffset(inst_data.src_node);
  24095     // zig fmt: off
  24096     const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  24097     const ptr_src     = block.builtinCallArgSrc(inst_data.src_node, 1);
  24098     const operand_src = block.builtinCallArgSrc(inst_data.src_node, 2);
  24099     const order_src   = block.builtinCallArgSrc(inst_data.src_node, 3);
  24100     // zig fmt: on
  24101     const operand = try sema.resolveInst(extra.operand);
  24102     const elem_ty = sema.typeOf(operand);
  24103     const uncasted_ptr = try sema.resolveInst(extra.ptr);
  24104     const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
  24105     const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
  24106 
  24107     const air_tag: Air.Inst.Tag = switch (order) {
  24108         .acquire, .acq_rel => {
  24109             return sema.fail(
  24110                 block,
  24111                 order_src,
  24112                 "@atomicStore atomic ordering must not be acquire or acq_rel",
  24113                 .{},
  24114             );
  24115         },
  24116         .unordered => .atomic_store_unordered,
  24117         .monotonic => .atomic_store_monotonic,
  24118         .release => .atomic_store_release,
  24119         .seq_cst => .atomic_store_seq_cst,
  24120     };
  24121 
  24122     return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
  24123 }
  24124 
  24125 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  24126     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24127     const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
  24128     const src = block.nodeOffset(inst_data.src_node);
  24129 
  24130     const mulend1_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  24131     const mulend2_src = block.builtinCallArgSrc(inst_data.src_node, 2);
  24132     const addend_src = block.builtinCallArgSrc(inst_data.src_node, 3);
  24133 
  24134     const addend = try sema.resolveInst(extra.addend);
  24135     const ty = sema.typeOf(addend);
  24136     const mulend1 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend1), mulend1_src);
  24137     const mulend2 = try sema.coerce(block, ty, try sema.resolveInst(extra.mulend2), mulend2_src);
  24138 
  24139     const maybe_mulend1 = try sema.resolveValue(mulend1);
  24140     const maybe_mulend2 = try sema.resolveValue(mulend2);
  24141     const maybe_addend = try sema.resolveValue(addend);
  24142     const pt = sema.pt;
  24143     const zcu = pt.zcu;
  24144 
  24145     switch (ty.scalarType(zcu).zigTypeTag(zcu)) {
  24146         .comptime_float, .float => {},
  24147         else => return sema.fail(block, src, "expected vector of floats or float type, found '{f}'", .{ty.fmt(pt)}),
  24148     }
  24149 
  24150     const runtime_src = if (maybe_mulend1) |mulend1_val| rs: {
  24151         if (maybe_mulend2) |mulend2_val| {
  24152             if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty);
  24153 
  24154             if (maybe_addend) |addend_val| {
  24155                 if (addend_val.isUndef(zcu)) return pt.undefRef(ty);
  24156                 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, pt);
  24157                 return Air.internedToRef(result_val.toIntern());
  24158             } else {
  24159                 break :rs addend_src;
  24160             }
  24161         } else {
  24162             if (maybe_addend) |addend_val| {
  24163                 if (addend_val.isUndef(zcu)) return pt.undefRef(ty);
  24164             }
  24165             break :rs mulend2_src;
  24166         }
  24167     } else rs: {
  24168         if (maybe_mulend2) |mulend2_val| {
  24169             if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty);
  24170         }
  24171         if (maybe_addend) |addend_val| {
  24172             if (addend_val.isUndef(zcu)) return pt.undefRef(ty);
  24173         }
  24174         break :rs mulend1_src;
  24175     };
  24176 
  24177     try sema.requireRuntimeBlock(block, src, runtime_src);
  24178     return block.addInst(.{
  24179         .tag = .mul_add,
  24180         .data = .{ .pl_op = .{
  24181             .operand = addend,
  24182             .payload = try sema.addExtra(Air.Bin{
  24183                 .lhs = mulend1,
  24184                 .rhs = mulend2,
  24185             }),
  24186         } },
  24187     });
  24188 }
  24189 
  24190 fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  24191     const tracy = trace(@src());
  24192     defer tracy.end();
  24193 
  24194     const pt = sema.pt;
  24195     const zcu = pt.zcu;
  24196     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24197     const modifier_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  24198     const func_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  24199     const args_src = block.builtinCallArgSrc(inst_data.src_node, 2);
  24200     const call_src = block.nodeOffset(inst_data.src_node);
  24201 
  24202     const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
  24203     const func = try sema.resolveInst(extra.callee);
  24204 
  24205     const modifier_ty = try sema.getBuiltinType(call_src, .CallModifier);
  24206     const air_ref = try sema.resolveInst(extra.modifier);
  24207     const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src);
  24208     const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{ .simple = .call_modifier });
  24209     var modifier = try sema.interpretBuiltinType(block, modifier_src, modifier_val, std.builtin.CallModifier);
  24210     switch (modifier) {
  24211         // These can be upgraded to comptime or nosuspend calls.
  24212         .auto, .never_tail, .no_suspend => {
  24213             if (block.isComptime()) {
  24214                 if (modifier == .never_tail) {
  24215                     return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});
  24216                 }
  24217                 modifier = .compile_time;
  24218             } else if (extra.flags.is_nosuspend) {
  24219                 modifier = .no_suspend;
  24220             }
  24221         },
  24222         // These can be upgraded to comptime. nosuspend bit can be safely ignored.
  24223         .always_inline, .compile_time => {
  24224             _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
  24225                 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)});
  24226             };
  24227 
  24228             if (block.isComptime()) {
  24229                 modifier = .compile_time;
  24230             }
  24231         },
  24232         .always_tail => {
  24233             if (block.isComptime()) {
  24234                 modifier = .compile_time;
  24235             }
  24236         },
  24237         .never_inline => {
  24238             if (block.isComptime()) {
  24239                 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});
  24240             }
  24241         },
  24242     }
  24243 
  24244     const args = try sema.resolveInst(extra.args);
  24245 
  24246     const args_ty = sema.typeOf(args);
  24247     if (!args_ty.isTuple(zcu)) {
  24248         return sema.fail(block, args_src, "expected a tuple, found '{f}'", .{args_ty.fmt(pt)});
  24249     }
  24250 
  24251     const resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(zcu));
  24252     for (resolved_args, 0..) |*resolved, i| {
  24253         resolved.* = try sema.tupleFieldValByIndex(block, args, @intCast(i), args_ty);
  24254     }
  24255 
  24256     const callee_ty = sema.typeOf(func);
  24257     const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false);
  24258     const ensure_result_used = extra.flags.ensure_result_used;
  24259     return sema.analyzeCall(
  24260         block,
  24261         func,
  24262         func_ty,
  24263         func_src,
  24264         call_src,
  24265         modifier,
  24266         ensure_result_used,
  24267         .{ .call_builtin = .{
  24268             .call_node_offset = inst_data.src_node,
  24269             .args = resolved_args,
  24270         } },
  24271         null,
  24272         .@"@call",
  24273     );
  24274 }
  24275 
  24276 fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  24277     const pt = sema.pt;
  24278     const zcu = pt.zcu;
  24279     const ip = &zcu.intern_pool;
  24280 
  24281     const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, extended.operand).data;
  24282     const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
  24283     const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small)));
  24284     assert(!flags.ptr_cast);
  24285     const inst_src = block.nodeOffset(extra.src_node);
  24286     const field_name_src = block.builtinCallArgSrc(extra.src_node, 0);
  24287     const field_ptr_src = block.builtinCallArgSrc(extra.src_node, 1);
  24288 
  24289     const parent_ptr_ty = try sema.resolveDestType(block, inst_src, extra.parent_ptr_type, .remove_eu, "@fieldParentPtr");
  24290     try sema.checkPtrType(block, inst_src, parent_ptr_ty, true);
  24291     const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
  24292     if (parent_ptr_info.flags.size != .one) {
  24293         return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)});
  24294     }
  24295     const parent_ty: Type = .fromInterned(parent_ptr_info.child);
  24296     switch (parent_ty.zigTypeTag(zcu)) {
  24297         .@"struct", .@"union" => {},
  24298         else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}),
  24299     }
  24300     try parent_ty.resolveLayout(pt);
  24301 
  24302     const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
  24303     const field_index = switch (parent_ty.zigTypeTag(zcu)) {
  24304         .@"struct" => blk: {
  24305             if (parent_ty.isTuple(zcu)) {
  24306                 if (field_name.eqlSlice("len", ip)) {
  24307                     return sema.fail(block, inst_src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
  24308                 }
  24309                 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, field_name_src);
  24310             } else {
  24311                 break :blk try sema.structFieldIndex(block, parent_ty, field_name, field_name_src);
  24312             }
  24313         },
  24314         .@"union" => try sema.unionFieldIndex(block, parent_ty, field_name, field_name_src),
  24315         else => unreachable,
  24316     };
  24317     if (parent_ty.zigTypeTag(zcu) == .@"struct" and parent_ty.structFieldIsComptime(field_index, zcu)) {
  24318         return sema.fail(block, field_name_src, "cannot get @fieldParentPtr of a comptime field", .{});
  24319     }
  24320 
  24321     const field_ptr = try sema.resolveInst(extra.field_ptr);
  24322     const field_ptr_ty = sema.typeOf(field_ptr);
  24323     try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty);
  24324     const field_ptr_info = field_ptr_ty.ptrInfo(zcu);
  24325 
  24326     var actual_parent_ptr_info: InternPool.Key.PtrType = .{
  24327         .child = parent_ty.toIntern(),
  24328         .flags = .{
  24329             .alignment = try parent_ptr_ty.ptrAlignmentSema(pt),
  24330             .is_const = field_ptr_info.flags.is_const,
  24331             .is_volatile = field_ptr_info.flags.is_volatile,
  24332             .is_allowzero = field_ptr_info.flags.is_allowzero,
  24333             .address_space = field_ptr_info.flags.address_space,
  24334         },
  24335         .packed_offset = parent_ptr_info.packed_offset,
  24336     };
  24337     const field_ty = parent_ty.fieldType(field_index, zcu);
  24338     var actual_field_ptr_info: InternPool.Key.PtrType = .{
  24339         .child = field_ty.toIntern(),
  24340         .flags = .{
  24341             .alignment = try field_ptr_ty.ptrAlignmentSema(pt),
  24342             .is_const = field_ptr_info.flags.is_const,
  24343             .is_volatile = field_ptr_info.flags.is_volatile,
  24344             .is_allowzero = field_ptr_info.flags.is_allowzero,
  24345             .address_space = field_ptr_info.flags.address_space,
  24346         },
  24347         .packed_offset = field_ptr_info.packed_offset,
  24348     };
  24349     switch (parent_ty.containerLayout(zcu)) {
  24350         .auto => {
  24351             actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(
  24352                 if (zcu.typeToStruct(parent_ty)) |struct_obj|
  24353                     try field_ty.structFieldAlignmentSema(
  24354                         struct_obj.fieldAlign(ip, field_index),
  24355                         struct_obj.layout,
  24356                         pt,
  24357                     )
  24358                 else if (zcu.typeToUnion(parent_ty)) |union_obj|
  24359                     try field_ty.unionFieldAlignmentSema(
  24360                         union_obj.fieldAlign(ip, field_index),
  24361                         union_obj.flagsUnordered(ip).layout,
  24362                         pt,
  24363                     )
  24364                 else
  24365                     actual_field_ptr_info.flags.alignment,
  24366             );
  24367 
  24368             actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
  24369             actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
  24370         },
  24371         .@"extern" => {
  24372             const field_offset = parent_ty.structFieldOffset(field_index, zcu);
  24373             actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (field_offset > 0)
  24374                 Alignment.fromLog2Units(@ctz(field_offset))
  24375             else
  24376                 actual_field_ptr_info.flags.alignment);
  24377 
  24378             actual_parent_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
  24379             actual_field_ptr_info.packed_offset = .{ .bit_offset = 0, .host_size = 0 };
  24380         },
  24381         .@"packed" => {
  24382             const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) +
  24383                 (if (zcu.typeToStruct(parent_ty)) |struct_obj| zcu.structPackedFieldBitOffset(struct_obj, field_index) else 0) -
  24384                 actual_field_ptr_info.packed_offset.bit_offset), 8) catch
  24385                 return sema.fail(block, inst_src, "pointer bit-offset mismatch", .{});
  24386             actual_parent_ptr_info.flags.alignment = actual_field_ptr_info.flags.alignment.minStrict(if (byte_offset > 0)
  24387                 Alignment.fromLog2Units(@ctz(byte_offset))
  24388             else
  24389                 actual_field_ptr_info.flags.alignment);
  24390         },
  24391     }
  24392 
  24393     const actual_field_ptr_ty = try pt.ptrTypeSema(actual_field_ptr_info);
  24394     const casted_field_ptr = try sema.coerce(block, actual_field_ptr_ty, field_ptr, field_ptr_src);
  24395     const actual_parent_ptr_ty = try pt.ptrTypeSema(actual_parent_ptr_info);
  24396 
  24397     const result = if (try sema.resolveDefinedValue(block, field_ptr_src, casted_field_ptr)) |field_ptr_val| result: {
  24398         switch (parent_ty.zigTypeTag(zcu)) {
  24399             .@"struct" => switch (parent_ty.containerLayout(zcu)) {
  24400                 .auto => {},
  24401                 .@"extern" => {
  24402                     const byte_offset = parent_ty.structFieldOffset(field_index, zcu);
  24403                     const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty);
  24404                     break :result Air.internedToRef(parent_ptr_val.toIntern());
  24405                 },
  24406                 .@"packed" => {
  24407                     // Logic lifted from type computation above - I'm just assuming it's correct.
  24408                     // `catch unreachable` since error case handled above.
  24409                     const byte_offset = std.math.divExact(u32, @abs(@as(i32, actual_parent_ptr_info.packed_offset.bit_offset) +
  24410                         zcu.structPackedFieldBitOffset(zcu.typeToStruct(parent_ty).?, field_index) -
  24411                         actual_field_ptr_info.packed_offset.bit_offset), 8) catch unreachable;
  24412                     const parent_ptr_val = try sema.ptrSubtract(block, field_ptr_src, field_ptr_val, byte_offset, actual_parent_ptr_ty);
  24413                     break :result Air.internedToRef(parent_ptr_val.toIntern());
  24414                 },
  24415             },
  24416             .@"union" => switch (parent_ty.containerLayout(zcu)) {
  24417                 .auto => {},
  24418                 .@"extern", .@"packed" => {
  24419                     // For an extern or packed union, just coerce the pointer.
  24420                     const parent_ptr_val = try pt.getCoerced(field_ptr_val, actual_parent_ptr_ty);
  24421                     break :result Air.internedToRef(parent_ptr_val.toIntern());
  24422                 },
  24423             },
  24424             else => unreachable,
  24425         }
  24426 
  24427         const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: {
  24428             const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) {
  24429                 .ptr => |ptr| ptr,
  24430                 else => break :opt_field null,
  24431             };
  24432             if (ptr.byte_offset != 0) break :opt_field null;
  24433             break :opt_field switch (ptr.base_addr) {
  24434                 .field => |field| field,
  24435                 else => null,
  24436             };
  24437         };
  24438 
  24439         const field = opt_field orelse {
  24440             return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
  24441         };
  24442 
  24443         if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) {
  24444             return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
  24445         }
  24446 
  24447         if (field.index != field_index) {
  24448             return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{
  24449                 field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt),
  24450             });
  24451         }
  24452         break :result try sema.coerce(block, actual_parent_ptr_ty, Air.internedToRef(field.base), inst_src);
  24453     } else result: {
  24454         try sema.requireRuntimeBlock(block, inst_src, field_ptr_src);
  24455         break :result try block.addInst(.{
  24456             .tag = .field_parent_ptr,
  24457             .data = .{ .ty_pl = .{
  24458                 .ty = Air.internedToRef(actual_parent_ptr_ty.toIntern()),
  24459                 .payload = try block.sema.addExtra(Air.FieldParentPtr{
  24460                     .field_ptr = casted_field_ptr,
  24461                     .field_index = @intCast(field_index),
  24462                 }),
  24463             } },
  24464         });
  24465     };
  24466     return sema.ptrCastFull(block, flags, inst_src, result, inst_src, parent_ptr_ty, "@fieldParentPtr");
  24467 }
  24468 
  24469 fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte_subtract: u64, new_ty: Type) !Value {
  24470     const pt = sema.pt;
  24471     const zcu = pt.zcu;
  24472     if (byte_subtract == 0) return pt.getCoerced(ptr_val, new_ty);
  24473     var ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
  24474         .undef => return sema.failWithUseOfUndef(block, src),
  24475         .ptr => |ptr| ptr,
  24476         else => unreachable,
  24477     };
  24478     if (ptr.byte_offset < byte_subtract) {
  24479         return sema.failWithOwnedErrorMsg(block, msg: {
  24480             const msg = try sema.errMsg(src, "pointer computation here causes illegal behavior", .{});
  24481             errdefer msg.destroy(sema.gpa);
  24482             try sema.errNote(src, msg, "resulting pointer exceeds bounds of containing value which may trigger overflow", .{});
  24483             break :msg msg;
  24484         });
  24485     }
  24486     ptr.byte_offset -= byte_subtract;
  24487     ptr.ty = new_ty.toIntern();
  24488     return Value.fromInterned(try pt.intern(.{ .ptr = ptr }));
  24489 }
  24490 
  24491 fn zirMinMax(
  24492     sema: *Sema,
  24493     block: *Block,
  24494     inst: Zir.Inst.Index,
  24495     comptime air_tag: Air.Inst.Tag,
  24496 ) CompileError!Air.Inst.Ref {
  24497     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24498     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  24499     const src = block.nodeOffset(inst_data.src_node);
  24500     const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  24501     const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  24502     const lhs = try sema.resolveInst(extra.lhs);
  24503     const rhs = try sema.resolveInst(extra.rhs);
  24504     return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src });
  24505 }
  24506 
  24507 fn zirMinMaxMulti(
  24508     sema: *Sema,
  24509     block: *Block,
  24510     extended: Zir.Inst.Extended.InstData,
  24511     comptime air_tag: Air.Inst.Tag,
  24512 ) CompileError!Air.Inst.Ref {
  24513     const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
  24514     const src_node = extra.data.src_node;
  24515     const src = block.nodeOffset(src_node);
  24516     const operands = sema.code.refSlice(extra.end, extended.small);
  24517 
  24518     const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
  24519     const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len);
  24520 
  24521     for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| {
  24522         op_src.* = block.builtinCallArgSrc(src_node, @intCast(i));
  24523         air_ref.* = try sema.resolveInst(zir_ref);
  24524     }
  24525 
  24526     return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs);
  24527 }
  24528 
  24529 fn analyzeMinMax(
  24530     sema: *Sema,
  24531     block: *Block,
  24532     src: LazySrcLoc,
  24533     comptime air_tag: Air.Inst.Tag,
  24534     operands: []const Air.Inst.Ref,
  24535     operand_srcs: []const LazySrcLoc,
  24536 ) CompileError!Air.Inst.Ref {
  24537     assert(operands.len == operand_srcs.len);
  24538     assert(operands.len > 0);
  24539 
  24540     const pt = sema.pt;
  24541     const zcu = pt.zcu;
  24542 
  24543     // This function has the signature `fn (Value, Value, *Zcu) Value`.
  24544     // It is only used on scalar values, although the values may have different types.
  24545     // If either operand is undef, it returns undef.
  24546     const opFunc = switch (air_tag) {
  24547         .min => Value.numberMin,
  24548         .max => Value.numberMax,
  24549         else => comptime unreachable,
  24550     };
  24551 
  24552     if (operands.len == 1) {
  24553         try sema.checkNumericType(block, operand_srcs[0], sema.typeOf(operands[0]));
  24554         return operands[0];
  24555     }
  24556 
  24557     // First, basic type validation; we'll make sure all the operands are numeric and agree on vector length.
  24558     // This value will be `null` for a scalar type, otherwise the length of the vector type.
  24559     const vector_len: ?u64 = vec_len: {
  24560         const first_operand_ty = sema.typeOf(operands[0]);
  24561         try sema.checkNumericType(block, operand_srcs[0], first_operand_ty);
  24562         if (first_operand_ty.zigTypeTag(zcu) == .vector) {
  24563             const vec_len = first_operand_ty.vectorLen(zcu);
  24564             for (operands[1..], operand_srcs[1..]) |operand, operand_src| {
  24565                 const operand_ty = sema.typeOf(operand);
  24566                 try sema.checkNumericType(block, operand_src, operand_ty);
  24567                 if (operand_ty.zigTypeTag(zcu) != .vector) {
  24568                     return sema.failWithOwnedErrorMsg(block, msg: {
  24569                         const msg = try sema.errMsg(operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)});
  24570                         errdefer msg.destroy(zcu.gpa);
  24571                         try sema.errNote(operand_srcs[0], msg, "vector operand here", .{});
  24572                         break :msg msg;
  24573                     });
  24574                 }
  24575                 if (operand_ty.vectorLen(zcu) != vec_len) {
  24576                     return sema.failWithOwnedErrorMsg(block, msg: {
  24577                         const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{f}'", .{ vec_len, operand_ty.fmt(pt) });
  24578                         errdefer msg.destroy(zcu.gpa);
  24579                         try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len});
  24580                         break :msg msg;
  24581                     });
  24582                 }
  24583             }
  24584             break :vec_len vec_len;
  24585         } else {
  24586             for (operands[1..], operand_srcs[1..]) |operand, operand_src| {
  24587                 const operand_ty = sema.typeOf(operand);
  24588                 try sema.checkNumericType(block, operand_src, operand_ty);
  24589                 if (operand_ty.zigTypeTag(zcu) == .vector) {
  24590                     return sema.failWithOwnedErrorMsg(block, msg: {
  24591                         const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{f}'", .{first_operand_ty.fmt(pt)});
  24592                         errdefer msg.destroy(zcu.gpa);
  24593                         try sema.errNote(operand_src, msg, "vector operand here", .{});
  24594                         break :msg msg;
  24595                     });
  24596                 }
  24597             }
  24598             break :vec_len null;
  24599         }
  24600     };
  24601 
  24602     // Now we want to look at the scalar types. If any is a float, our result will be a float. This
  24603     // union is in "priority" order: `float` overrides `comptime_float` overrides `int`.
  24604     const TypeStrat = union(enum) {
  24605         float: Type,
  24606         comptime_float,
  24607         int: struct {
  24608             /// If this is still `true` at the end, we will just use a `comptime_int`.
  24609             all_comptime_int: bool,
  24610             // These two fields tells us about the *result* type, which is refined based on operand types.
  24611             // e.g. `@max(u32, i64)` results in a `u63`, because the result is >=0 and <=maxInt(i64).
  24612             result_min: Value,
  24613             result_max: Value,
  24614             // These two fields tell us the *intermediate* type to use for actually computing the min/max.
  24615             // e.g. `@max(u32, i64)` uses an intermediate `i64`, because it can fit all our operands.
  24616             operand_min: Value,
  24617             operand_max: Value,
  24618         },
  24619         none,
  24620     };
  24621     var cur_strat: TypeStrat = .none;
  24622     for (operands) |operand| {
  24623         const operand_scalar_ty = sema.typeOf(operand).scalarType(zcu);
  24624         const want_strat: TypeStrat = switch (operand_scalar_ty.zigTypeTag(zcu)) {
  24625             .comptime_int => s: {
  24626                 const val = (try sema.resolveValueResolveLazy(operand)).?;
  24627                 if (val.isUndef(zcu)) break :s .none;
  24628                 break :s .{ .int = .{
  24629                     .all_comptime_int = true,
  24630                     .result_min = val,
  24631                     .result_max = val,
  24632                     .operand_min = val,
  24633                     .operand_max = val,
  24634                 } };
  24635             },
  24636             .comptime_float => .comptime_float,
  24637             .float => .{ .float = operand_scalar_ty },
  24638             .int => s: {
  24639                 // If the *value* is comptime-known, we will use that to get tighter bounds. If #3806
  24640                 // is accepted and implemented, so that integer literals have a tightly-bounded ranged
  24641                 // integer type (and `comptime_int` ceases to exist), this block should probably go away
  24642                 // (replaced with just the simple calls to `Type.minInt`/`Type.maxInt`) so that we only
  24643                 // use the input *types* to determine the result type.
  24644                 const min: Value, const max: Value = bounds: {
  24645                     if (try sema.resolveValueResolveLazy(operand)) |operand_val| {
  24646                         if (vector_len) |len| {
  24647                             var min = try operand_val.elemValue(pt, 0);
  24648                             var max = min;
  24649                             for (1..@intCast(len)) |elem_idx| {
  24650                                 const elem_val = try operand_val.elemValue(pt, elem_idx);
  24651                                 min = Value.numberMin(min, elem_val, zcu);
  24652                                 max = Value.numberMax(max, elem_val, zcu);
  24653                             }
  24654                             if (!min.isUndef(zcu) and !max.isUndef(zcu)) {
  24655                                 break :bounds .{ min, max };
  24656                             }
  24657                         } else {
  24658                             if (!operand_val.isUndef(zcu)) {
  24659                                 break :bounds .{ operand_val, operand_val };
  24660                             }
  24661                         }
  24662                     }
  24663                     break :bounds .{
  24664                         try operand_scalar_ty.minInt(pt, operand_scalar_ty),
  24665                         try operand_scalar_ty.maxInt(pt, operand_scalar_ty),
  24666                     };
  24667                 };
  24668                 break :s .{ .int = .{
  24669                     .all_comptime_int = false,
  24670                     .result_min = min,
  24671                     .result_max = max,
  24672                     .operand_min = min,
  24673                     .operand_max = max,
  24674                 } };
  24675             },
  24676             else => unreachable,
  24677         };
  24678         if (@intFromEnum(want_strat) < @intFromEnum(cur_strat)) {
  24679             // `want_strat` overrides `cur_strat`.
  24680             cur_strat = want_strat;
  24681         } else if (@intFromEnum(want_strat) == @intFromEnum(cur_strat)) {
  24682             // The behavior depends on the tag.
  24683             switch (cur_strat) {
  24684                 .none, .comptime_float => {}, // no payload, so nop
  24685                 .float => |cur_float| {
  24686                     const want_float = want_strat.float;
  24687                     // Select the larger bit size. If the bit size is the same, select whichever is not c_longdouble.
  24688                     const cur_bits = cur_float.floatBits(zcu.getTarget());
  24689                     const want_bits = want_float.floatBits(zcu.getTarget());
  24690                     if (want_bits > cur_bits or
  24691                         (want_bits == cur_bits and
  24692                             cur_float.toIntern() == .c_longdouble_type and
  24693                             want_float.toIntern() != .c_longdouble_type))
  24694                     {
  24695                         cur_strat = want_strat;
  24696                     }
  24697                 },
  24698                 .int => |*cur_int| {
  24699                     const want_int = want_strat.int;
  24700                     if (!want_int.all_comptime_int) cur_int.all_comptime_int = false;
  24701                     cur_int.result_min = opFunc(cur_int.result_min, want_int.result_min, zcu);
  24702                     cur_int.result_max = opFunc(cur_int.result_max, want_int.result_max, zcu);
  24703                     cur_int.operand_min = Value.numberMin(cur_int.operand_min, want_int.operand_min, zcu);
  24704                     cur_int.operand_max = Value.numberMax(cur_int.operand_max, want_int.operand_max, zcu);
  24705                 },
  24706             }
  24707         }
  24708     }
  24709 
  24710     // Use `cur_strat` to actually resolve the result type (and intermediate type).
  24711     const result_scalar_ty: Type, const intermediate_scalar_ty: Type = switch (cur_strat) {
  24712         .float => |ty| .{ ty, ty },
  24713         .comptime_float => .{ .comptime_float, .comptime_float },
  24714         .int => |int| if (int.all_comptime_int) .{
  24715             .comptime_int,
  24716             .comptime_int,
  24717         } else .{
  24718             try pt.intFittingRange(int.result_min, int.result_max),
  24719             try pt.intFittingRange(int.operand_min, int.operand_max),
  24720         },
  24721         .none => .{ .comptime_int, .comptime_int }, // all undef comptime ints
  24722     };
  24723     const result_ty: Type = if (vector_len) |l| try pt.vectorType(.{
  24724         .len = @intCast(l),
  24725         .child = result_scalar_ty.toIntern(),
  24726     }) else result_scalar_ty;
  24727     const intermediate_ty: Type = if (vector_len) |l| try pt.vectorType(.{
  24728         .len = @intCast(l),
  24729         .child = intermediate_scalar_ty.toIntern(),
  24730     }) else intermediate_scalar_ty;
  24731 
  24732     // This value, if not `null`, will have type `intermediate_ty`.
  24733     const comptime_part: ?Value = ct: {
  24734         // Contains the comptime-known scalar result values.
  24735         // Values are scalars with no particular type.
  24736         // `elems.len` is `vector_len orelse 1`.
  24737         const elems: []InternPool.Index = try sema.arena.alloc(
  24738             InternPool.Index,
  24739             try sema.usizeCast(block, src, vector_len orelse 1),
  24740         );
  24741         // If `false`, we've not seen any comptime-known operand yet, so `elems` contains `undefined`.
  24742         // Otherwise, `elems` is populated with the comptime-known results so far.
  24743         var elems_populated = false;
  24744         // Populated when we see a runtime-known operand.
  24745         var opt_runtime_src: ?LazySrcLoc = null;
  24746 
  24747         for (operands, operand_srcs) |operand, operand_src| {
  24748             const operand_val = try sema.resolveValueResolveLazy(operand) orelse {
  24749                 if (opt_runtime_src == null) opt_runtime_src = operand_src;
  24750                 continue;
  24751             };
  24752             if (vector_len) |len| {
  24753                 // Vector case; apply `opFunc` to each element.
  24754                 if (elems_populated) {
  24755                     for (elems, 0..@intCast(len)) |*elem, elem_idx| {
  24756                         const new_elem = try operand_val.elemValue(pt, elem_idx);
  24757                         elem.* = opFunc(.fromInterned(elem.*), new_elem, zcu).toIntern();
  24758                     }
  24759                 } else {
  24760                     elems_populated = true;
  24761                     for (elems, 0..@intCast(len)) |*elem_out, elem_idx| {
  24762                         elem_out.* = (try operand_val.elemValue(pt, elem_idx)).toIntern();
  24763                     }
  24764                 }
  24765             } else {
  24766                 // Scalar case; just apply `opFunc`.
  24767                 if (elems_populated) {
  24768                     elems[0] = opFunc(.fromInterned(elems[0]), operand_val, zcu).toIntern();
  24769                 } else {
  24770                     elems_populated = true;
  24771                     elems[0] = operand_val.toIntern();
  24772                 }
  24773             }
  24774         }
  24775         const runtime_src = opt_runtime_src orelse {
  24776             // The result is comptime-known. Coerce each element to its scalar type.
  24777             assert(elems_populated);
  24778             for (elems) |*elem| {
  24779                 if (Value.fromInterned(elem.*).isUndef(zcu)) {
  24780                     elem.* = (try pt.undefValue(result_scalar_ty)).toIntern();
  24781                 } else {
  24782                     // This coercion will always succeed, because `result_scalar_ty` can definitely hold the result.
  24783                     const coerced_ref = try sema.coerce(block, result_scalar_ty, Air.internedToRef(elem.*), .unneeded);
  24784                     elem.* = coerced_ref.toInterned().?;
  24785                 }
  24786             }
  24787             if (vector_len == null) return Air.internedToRef(elems[0]);
  24788             return Air.internedToRef(try pt.intern(.{ .aggregate = .{
  24789                 .ty = result_ty.toIntern(),
  24790                 .storage = .{ .elems = elems },
  24791             } }));
  24792         };
  24793         _ = runtime_src;
  24794         // The result is runtime-known.
  24795         // Coerce each element to the intermediate scalar type, unless there were no comptime-known operands.
  24796         if (!elems_populated) break :ct null;
  24797         for (elems) |*elem| {
  24798             if (Value.fromInterned(elem.*).isUndef(zcu)) {
  24799                 elem.* = (try pt.undefValue(intermediate_scalar_ty)).toIntern();
  24800             } else {
  24801                 // This coercion will always succeed, because `intermediate_scalar_ty` can definitely hold all operands.
  24802                 const coerced_ref = try sema.coerce(block, intermediate_scalar_ty, Air.internedToRef(elem.*), .unneeded);
  24803                 elem.* = coerced_ref.toInterned().?;
  24804             }
  24805         }
  24806         break :ct .fromInterned(if (vector_len != null) try pt.intern(.{ .aggregate = .{
  24807             .ty = intermediate_ty.toIntern(),
  24808             .storage = .{ .elems = elems },
  24809         } }) else elems[0]);
  24810     };
  24811 
  24812     // 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.
  24813 
  24814     // `.none` indicates no result so far.
  24815     var cur_result: Air.Inst.Ref = if (comptime_part) |val| Air.internedToRef(val.toIntern()) else .none;
  24816     for (operands, operand_srcs) |operand, operand_src| {
  24817         if (try sema.isComptimeKnown(operand)) continue; // already in `comptime_part`
  24818         // This coercion could fail; e.g. coercing a runtime integer peer to a `comptime_float` in a case like `@min(runtime_int, 1.5)`.
  24819         const operand_coerced = try sema.coerce(block, intermediate_ty, operand, operand_src);
  24820         if (cur_result == .none) {
  24821             cur_result = operand_coerced;
  24822         } else {
  24823             cur_result = try block.addBinOp(air_tag, cur_result, operand_coerced);
  24824         }
  24825     }
  24826 
  24827     assert(cur_result != .none);
  24828     assert(sema.typeOf(cur_result).toIntern() == intermediate_ty.toIntern());
  24829 
  24830     // 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.
  24831     if (comptime_part) |val| {
  24832         if (val.isUndefDeep(zcu)) {
  24833             return pt.undefRef(result_ty);
  24834         }
  24835     }
  24836 
  24837     if (result_ty.toIntern() == intermediate_ty.toIntern()) {
  24838         // No final cast needed; we're all done.
  24839         return cur_result;
  24840     }
  24841 
  24842     // A final cast is needed. The only case where `intermediate_ty` is different is for integers,
  24843     // where we have refined the range, so we should be doing an intcast.
  24844     assert(intermediate_scalar_ty.zigTypeTag(zcu) == .int);
  24845     assert(result_scalar_ty.zigTypeTag(zcu) == .int);
  24846     return block.addTyOp(.intcast, result_ty, cur_result);
  24847 }
  24848 
  24849 fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref {
  24850     const pt = sema.pt;
  24851     const zcu = pt.zcu;
  24852     const ptr_ty = sema.typeOf(ptr);
  24853     const info = ptr_ty.ptrInfo(zcu);
  24854     if (info.flags.size == .one) {
  24855         // Already an array pointer.
  24856         return ptr;
  24857     }
  24858     const new_ty = try pt.ptrTypeSema(.{
  24859         .child = (try pt.arrayType(.{
  24860             .len = len,
  24861             .sentinel = info.sentinel,
  24862             .child = info.child,
  24863         })).toIntern(),
  24864         .flags = .{
  24865             .alignment = info.flags.alignment,
  24866             .is_const = info.flags.is_const,
  24867             .is_volatile = info.flags.is_volatile,
  24868             .is_allowzero = info.flags.is_allowzero,
  24869             .address_space = info.flags.address_space,
  24870         },
  24871     });
  24872     const non_slice_ptr = if (info.flags.size == .slice)
  24873         try block.addTyOp(.slice_ptr, ptr_ty.slicePtrFieldType(zcu), ptr)
  24874     else
  24875         ptr;
  24876     return block.addBitCast(new_ty, non_slice_ptr);
  24877 }
  24878 
  24879 fn zirMemcpy(
  24880     sema: *Sema,
  24881     block: *Block,
  24882     inst: Zir.Inst.Index,
  24883     air_tag: Air.Inst.Tag,
  24884     check_aliasing: bool,
  24885 ) CompileError!void {
  24886     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  24887     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  24888     const src = block.nodeOffset(inst_data.src_node);
  24889     const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  24890     const src_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  24891     const dest_ptr = try sema.resolveInst(extra.lhs);
  24892     const src_ptr = try sema.resolveInst(extra.rhs);
  24893     const dest_ty = sema.typeOf(dest_ptr);
  24894     const src_ty = sema.typeOf(src_ptr);
  24895     const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr);
  24896     const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
  24897     const pt = sema.pt;
  24898     const zcu = pt.zcu;
  24899 
  24900     if (dest_ty.isConstPtr(zcu)) {
  24901         return sema.fail(block, dest_src, "cannot copy to constant pointer", .{});
  24902     }
  24903 
  24904     if (dest_len == .none and src_len == .none) {
  24905         const msg = msg: {
  24906             const msg = try sema.errMsg(src, "unknown copy length", .{});
  24907             errdefer msg.destroy(sema.gpa);
  24908             try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{
  24909                 dest_ty.fmt(pt),
  24910             });
  24911             try sema.errNote(src_src, msg, "source type '{f}' provides no length", .{
  24912                 src_ty.fmt(pt),
  24913             });
  24914             break :msg msg;
  24915         };
  24916         return sema.failWithOwnedErrorMsg(block, msg);
  24917     }
  24918 
  24919     const dest_elem_ty = dest_ty.indexablePtrElem(zcu);
  24920     const src_elem_ty = src_ty.indexablePtrElem(zcu);
  24921 
  24922     const imc = try sema.coerceInMemoryAllowed(
  24923         block,
  24924         dest_elem_ty,
  24925         src_elem_ty,
  24926         false,
  24927         zcu.getTarget(),
  24928         dest_src,
  24929         src_src,
  24930         null,
  24931     );
  24932     if (imc != .ok) return sema.failWithOwnedErrorMsg(block, msg: {
  24933         const msg = try sema.errMsg(
  24934             src,
  24935             "pointer element type '{f}' cannot coerce into element type '{f}'",
  24936             .{ src_elem_ty.fmt(pt), dest_elem_ty.fmt(pt) },
  24937         );
  24938         errdefer msg.destroy(sema.gpa);
  24939         try imc.report(sema, src, msg);
  24940         break :msg msg;
  24941     });
  24942 
  24943     var len_val: ?Value = null;
  24944 
  24945     if (dest_len != .none and src_len != .none) check: {
  24946         // If we can check at compile-time, no need for runtime safety.
  24947         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  24948             len_val = dest_len_val;
  24949             if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  24950                 if (!(try sema.valuesEqual(dest_len_val, src_len_val, .usize))) {
  24951                     const msg = msg: {
  24952                         const msg = try sema.errMsg(src, "non-matching copy lengths", .{});
  24953                         errdefer msg.destroy(sema.gpa);
  24954                         try sema.errNote(dest_src, msg, "length {f} here", .{
  24955                             dest_len_val.fmtValueSema(pt, sema),
  24956                         });
  24957                         try sema.errNote(src_src, msg, "length {f} here", .{
  24958                             src_len_val.fmtValueSema(pt, sema),
  24959                         });
  24960                         break :msg msg;
  24961                     };
  24962                     return sema.failWithOwnedErrorMsg(block, msg);
  24963                 }
  24964                 break :check;
  24965             }
  24966         } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  24967             len_val = src_len_val;
  24968         }
  24969 
  24970         if (block.wantSafety()) {
  24971             const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
  24972             try sema.addSafetyCheck(block, src, ok, .copy_len_mismatch);
  24973         }
  24974     } else if (dest_len != .none) {
  24975         if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
  24976             len_val = dest_len_val;
  24977         }
  24978     } else if (src_len != .none) {
  24979         if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
  24980             len_val = src_len_val;
  24981         }
  24982     }
  24983 
  24984     zero_bit: {
  24985         const src_comptime = try src_elem_ty.comptimeOnlySema(pt);
  24986         const dest_comptime = try dest_elem_ty.comptimeOnlySema(pt);
  24987         assert(src_comptime == dest_comptime); // IMC
  24988         if (src_comptime) break :zero_bit;
  24989 
  24990         const src_has_bits = try src_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt);
  24991         const dest_has_bits = try dest_elem_ty.hasRuntimeBitsIgnoreComptimeSema(pt);
  24992         assert(src_has_bits == dest_has_bits); // IMC
  24993         if (src_has_bits) break :zero_bit;
  24994 
  24995         // The element type is zero-bit. We've done all validation (aside from the aliasing check,
  24996         // which we must skip) so we're done.
  24997         return;
  24998     }
  24999 
  25000     const runtime_src = rs: {
  25001         const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src;
  25002         const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src;
  25003 
  25004         const raw_dest_ptr = if (dest_ty.isSlice(zcu)) dest_ptr_val.slicePtr(zcu) else dest_ptr_val;
  25005         const raw_src_ptr = if (src_ty.isSlice(zcu)) src_ptr_val.slicePtr(zcu) else src_ptr_val;
  25006 
  25007         const len_u64 = try len_val.?.toUnsignedIntSema(pt);
  25008 
  25009         if (check_aliasing) {
  25010             if (Value.doPointersOverlap(
  25011                 raw_src_ptr,
  25012                 raw_dest_ptr,
  25013                 len_u64,
  25014                 zcu,
  25015             )) return sema.fail(block, src, "'@memcpy' arguments alias", .{});
  25016         }
  25017 
  25018         if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src;
  25019 
  25020         // Because comptime pointer access is a somewhat expensive operation, we implement @memcpy
  25021         // as one load and store of an array, rather than N loads and stores of individual elements.
  25022 
  25023         const array_ty = try pt.arrayType(.{
  25024             .child = dest_elem_ty.toIntern(),
  25025             .len = len_u64,
  25026         });
  25027 
  25028         const dest_array_ptr_ty = try pt.ptrType(info: {
  25029             var info = dest_ty.ptrInfo(zcu);
  25030             info.flags.size = .one;
  25031             info.child = array_ty.toIntern();
  25032             info.sentinel = .none;
  25033             break :info info;
  25034         });
  25035         const src_array_ptr_ty = try pt.ptrType(info: {
  25036             var info = src_ty.ptrInfo(zcu);
  25037             info.flags.size = .one;
  25038             info.child = array_ty.toIntern();
  25039             info.sentinel = .none;
  25040             break :info info;
  25041         });
  25042 
  25043         const coerced_dest_ptr = try pt.getCoerced(raw_dest_ptr, dest_array_ptr_ty);
  25044         const coerced_src_ptr = try pt.getCoerced(raw_src_ptr, src_array_ptr_ty);
  25045 
  25046         const array_val = try sema.pointerDeref(block, src_src, coerced_src_ptr, src_array_ptr_ty) orelse break :rs src_src;
  25047         try sema.storePtrVal(block, dest_src, coerced_dest_ptr, array_val, array_ty);
  25048         return;
  25049     };
  25050 
  25051     // If the length is comptime-known, then upgrade src and destination types
  25052     // into pointer-to-array. At this point we know they are both pointers
  25053     // already.
  25054     var new_dest_ptr = dest_ptr;
  25055     var new_src_ptr = src_ptr;
  25056     if (len_val) |val| {
  25057         const len = try val.toUnsignedIntSema(pt);
  25058         if (len == 0) {
  25059             // This AIR instruction guarantees length > 0 if it is comptime-known.
  25060             return;
  25061         }
  25062         new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len);
  25063         new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
  25064     }
  25065 
  25066     if (dest_len != .none) {
  25067         // Change the src from slice to a many pointer, to avoid multiple ptr
  25068         // slice extractions in AIR instructions.
  25069         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  25070         if (new_src_ptr_ty.isSlice(zcu)) {
  25071             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  25072         }
  25073     } else if (dest_len == .none and len_val == null) {
  25074         // Change the dest to a slice, since its type must have the length.
  25075         const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr);
  25076         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);
  25077         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  25078         if (new_src_ptr_ty.isSlice(zcu)) {
  25079             new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
  25080         }
  25081     }
  25082 
  25083     try sema.requireRuntimeBlock(block, src, runtime_src);
  25084     try sema.validateRuntimeValue(block, dest_src, dest_ptr);
  25085     try sema.validateRuntimeValue(block, src_src, src_ptr);
  25086 
  25087     // Aliasing safety check.
  25088     if (check_aliasing and block.wantSafety()) {
  25089         const len = if (len_val) |v|
  25090             Air.internedToRef(v.toIntern())
  25091         else if (dest_len != .none)
  25092             dest_len
  25093         else
  25094             src_len;
  25095 
  25096         // Extract raw pointer from dest slice. The AIR instructions could support them, but
  25097         // it would cause redundant machine code instructions.
  25098         const new_dest_ptr_ty = sema.typeOf(new_dest_ptr);
  25099         const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(zcu))
  25100             try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty)
  25101         else if (new_dest_ptr_ty.ptrSize(zcu) == .one) ptr: {
  25102             var dest_manyptr_ty_key = zcu.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type;
  25103             assert(dest_manyptr_ty_key.flags.size == .one);
  25104             dest_manyptr_ty_key.child = dest_elem_ty.toIntern();
  25105             dest_manyptr_ty_key.flags.size = .many;
  25106             break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(dest_manyptr_ty_key), new_dest_ptr, dest_src);
  25107         } else new_dest_ptr;
  25108 
  25109         const new_src_ptr_ty = sema.typeOf(new_src_ptr);
  25110         const raw_src_ptr = if (new_src_ptr_ty.isSlice(zcu))
  25111             try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty)
  25112         else if (new_src_ptr_ty.ptrSize(zcu) == .one) ptr: {
  25113             var src_manyptr_ty_key = zcu.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type;
  25114             assert(src_manyptr_ty_key.flags.size == .one);
  25115             src_manyptr_ty_key.child = src_elem_ty.toIntern();
  25116             src_manyptr_ty_key.flags.size = .many;
  25117             break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(src_manyptr_ty_key), new_src_ptr, src_src);
  25118         } else new_src_ptr;
  25119 
  25120         // ok1: dest >= src + len
  25121         // ok2: src >= dest + len
  25122         const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src_src, src);
  25123         const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
  25124         const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
  25125         const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
  25126         const ok = try block.addBinOp(.bool_or, ok1, ok2);
  25127         try sema.addSafetyCheck(block, src, ok, .memcpy_alias);
  25128     }
  25129 
  25130     _ = try block.addInst(.{
  25131         .tag = air_tag,
  25132         .data = .{ .bin_op = .{
  25133             .lhs = new_dest_ptr,
  25134             .rhs = new_src_ptr,
  25135         } },
  25136     });
  25137 }
  25138 
  25139 fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
  25140     const pt = sema.pt;
  25141     const zcu = pt.zcu;
  25142     const gpa = sema.gpa;
  25143     const ip = &zcu.intern_pool;
  25144     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  25145     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
  25146     const src = block.nodeOffset(inst_data.src_node);
  25147     const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0);
  25148     const value_src = block.builtinCallArgSrc(inst_data.src_node, 1);
  25149     const dest_ptr = try sema.resolveInst(extra.lhs);
  25150     const uncoerced_elem = try sema.resolveInst(extra.rhs);
  25151     const dest_ptr_ty = sema.typeOf(dest_ptr);
  25152     try checkMemOperand(sema, block, dest_src, dest_ptr_ty);
  25153 
  25154     if (dest_ptr_ty.isConstPtr(zcu)) {
  25155         return sema.fail(block, dest_src, "cannot memset constant pointer", .{});
  25156     }
  25157 
  25158     const dest_elem_ty: Type = dest_elem_ty: {
  25159         const ptr_info = dest_ptr_ty.ptrInfo(zcu);
  25160         switch (ptr_info.flags.size) {
  25161             .slice => break :dest_elem_ty .fromInterned(ptr_info.child),
  25162             .one => {
  25163                 if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) {
  25164                     break :dest_elem_ty Type.fromInterned(ptr_info.child).childType(zcu);
  25165                 }
  25166             },
  25167             .many, .c => {},
  25168         }
  25169         return sema.failWithOwnedErrorMsg(block, msg: {
  25170             const msg = try sema.errMsg(src, "unknown @memset length", .{});
  25171             errdefer msg.destroy(sema.gpa);
  25172             try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{
  25173                 dest_ptr_ty.fmt(pt),
  25174             });
  25175             break :msg msg;
  25176         });
  25177     };
  25178 
  25179     const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src);
  25180 
  25181     const runtime_src = rs: {
  25182         const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src;
  25183         const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, pt.tid, "len", .no_embedded_nulls), dest_src);
  25184         const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src;
  25185         const len_u64 = try len_val.toUnsignedIntSema(pt);
  25186         const len = try sema.usizeCast(block, dest_src, len_u64);
  25187         if (len == 0) {
  25188             // This AIR instruction guarantees length > 0 if it is comptime-known.
  25189             return;
  25190         }
  25191 
  25192         if (!sema.isComptimeMutablePtr(ptr_val)) break :rs dest_src;
  25193         const elem_val = try sema.resolveValue(elem) orelse break :rs value_src;
  25194         const array_ty = try pt.arrayType(.{
  25195             .child = dest_elem_ty.toIntern(),
  25196             .len = len_u64,
  25197         });
  25198         const array_val = Value.fromInterned(try pt.intern(.{ .aggregate = .{
  25199             .ty = array_ty.toIntern(),
  25200             .storage = .{ .repeated_elem = elem_val.toIntern() },
  25201         } }));
  25202         const array_ptr_ty = ty: {
  25203             var info = dest_ptr_ty.ptrInfo(zcu);
  25204             info.flags.size = .one;
  25205             info.child = array_ty.toIntern();
  25206             break :ty try pt.ptrType(info);
  25207         };
  25208         const raw_ptr_val = if (dest_ptr_ty.isSlice(zcu)) ptr_val.slicePtr(zcu) else ptr_val;
  25209         const array_ptr_val = try pt.getCoerced(raw_ptr_val, array_ptr_ty);
  25210         return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty);
  25211     };
  25212 
  25213     try sema.requireRuntimeBlock(block, src, runtime_src);
  25214     try sema.validateRuntimeValue(block, dest_src, dest_ptr);
  25215     try sema.validateRuntimeValue(block, value_src, elem);
  25216 
  25217     _ = try block.addInst(.{
  25218         .tag = if (block.wantSafety()) .memset_safe else .memset,
  25219         .data = .{ .bin_op = .{
  25220             .lhs = dest_ptr,
  25221             .rhs = elem,
  25222         } },
  25223     });
  25224 }
  25225 
  25226 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  25227     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
  25228     const src = block.nodeOffset(inst_data.src_node);
  25229     return sema.failWithUseOfAsync(block, src);
  25230 }
  25231 
  25232 fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
  25233     const tracy = trace(@src());
  25234     defer tracy.end();
  25235 
  25236     const pt = sema.pt;
  25237     const zcu = pt.zcu;
  25238     const ip = &zcu.intern_pool;
  25239     const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
  25240     const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
  25241     const target = zcu.getTarget();
  25242 
  25243     const cc_src = block.src(.{ .node_offset_fn_type_cc = inst_data.src_node });
  25244     const ret_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node });
  25245     const has_body = extra.data.body_len != 0;
  25246 
  25247     var extra_index: usize = extra.end;
  25248 
  25249     const cc: std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
  25250         const body_len = sema.code.extra[extra_index];
  25251         extra_index += 1;
  25252         const body = sema.code.bodySlice(extra_index, body_len);
  25253         extra_index += body.len;
  25254 
  25255         const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention);
  25256         const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, .{ .simple = .@"callconv" });
  25257         break :blk try sema.analyzeValueAsCallconv(block, cc_src, val);
  25258     } else if (extra.data.bits.has_cc_ref) blk: {
  25259         const cc_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  25260         extra_index += 1;
  25261         const cc_ty = try sema.getBuiltinType(cc_src, .CallingConvention);
  25262         const uncoerced_cc = try sema.resolveInst(cc_ref);
  25263         const coerced_cc = try sema.coerce(block, cc_ty, uncoerced_cc, cc_src);
  25264         const cc_val = try sema.resolveConstDefinedValue(block, cc_src, coerced_cc, .{ .simple = .@"callconv" });
  25265         break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val);
  25266     } else cc: {
  25267         if (has_body) {
  25268             const func_decl_nav = sema.owner.unwrap().nav_val;
  25269             const func_decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(&zcu.intern_pool) orelse return error.AnalysisFail;
  25270             const zir_decl = sema.code.getDeclaration(func_decl_inst);
  25271             if (zir_decl.linkage == .@"export") {
  25272                 break :cc target.cCallingConvention() orelse {
  25273                     // This target has no default C calling convention. We sometimes trigger a similar
  25274                     // error by trying to evaluate `std.builtin.CallingConvention.c`, so for consistency,
  25275                     // let's eval that now and just get the transitive error. (It's guaranteed to error
  25276                     // because it does the exact `cCallingConvention` call we just did.)
  25277                     const cc_type = try sema.getBuiltinType(cc_src, .CallingConvention);
  25278                     _ = try sema.namespaceLookupVal(
  25279                         block,
  25280                         LazySrcLoc.unneeded,
  25281                         cc_type.getNamespaceIndex(zcu),
  25282                         try ip.getOrPutString(sema.gpa, pt.tid, "c", .no_embedded_nulls),
  25283                     );
  25284                     // The above should have errored.
  25285                     @panic("std.builtin is corrupt");
  25286                 };
  25287             }
  25288         }
  25289         break :cc .auto;
  25290     };
  25291 
  25292     const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
  25293         const body_len = sema.code.extra[extra_index];
  25294         extra_index += 1;
  25295         const body = sema.code.bodySlice(extra_index, body_len);
  25296         extra_index += body.len;
  25297         if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison;
  25298 
  25299         const val = try sema.resolveGenericBody(block, ret_src, body, inst, .type, .{ .simple = .function_ret_ty });
  25300         const ty = val.toType();
  25301         break :blk ty;
  25302     } else if (extra.data.bits.has_ret_ty_ref) blk: {
  25303         const ret_ty_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
  25304         extra_index += 1;
  25305         if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison;
  25306 
  25307         const ret_ty_air_ref = try sema.resolveInst(ret_ty_ref);
  25308         const ret_ty_val = try sema.resolveConstDefinedValue(block, ret_src, ret_ty_air_ref, .{ .simple = .function_ret_ty });
  25309         break :blk ret_ty_val.toType();
  25310     } else .void;
  25311 
  25312     const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: {
  25313         const x = sema.code.extra[extra_index];
  25314         extra_index += 1;
  25315         break :blk x;
  25316     } else 0;
  25317 
  25318     var src_locs: Zir.Inst.Func.SrcLocs = undefined;
  25319     if (has_body) {
  25320         extra_index += extra.data.body_len;
  25321         src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
  25322     }
  25323 
  25324     const is_var_args = extra.data.bits.is_var_args;
  25325     const is_inferred_error = extra.data.bits.is_inferred_error;
  25326     const is_noinline = extra.data.bits.is_noinline;
  25327 
  25328     return sema.funcCommon(
  25329         block,
  25330         inst_data.src_node,
  25331         inst,
  25332         cc,
  25333         ret_ty,
  25334         is_var_args,
  25335         is_inferred_error,
  25336         has_body,
  25337         src_locs,
  25338         noalias_bits,
  25339         is_noinline,
  25340     );
  25341 }
  25342 
  25343 fn zirCUndef(
  25344     sema: *Sema,
  25345     block: *Block,
  25346     extended: Zir.Inst.Extended.InstData,
  25347 ) CompileError!Air.Inst.Ref {
  25348     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25349     const src = block.builtinCallArgSrc(extra.node, 0);
  25350 
  25351     const name = try sema.resolveConstString(block, src, extra.operand, .{ .simple = .operand_cUndef_macro_name });
  25352     try block.c_import_buf.?.print("#undef {s}\n", .{name});
  25353     return .void_value;
  25354 }
  25355 
  25356 fn zirCInclude(
  25357     sema: *Sema,
  25358     block: *Block,
  25359     extended: Zir.Inst.Extended.InstData,
  25360 ) CompileError!Air.Inst.Ref {
  25361     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25362     const src = block.builtinCallArgSrc(extra.node, 0);
  25363 
  25364     const name = try sema.resolveConstString(block, src, extra.operand, .{ .simple = .operand_cInclude_file_name });
  25365     try block.c_import_buf.?.print("#include <{s}>\n", .{name});
  25366     return .void_value;
  25367 }
  25368 
  25369 fn zirCDefine(
  25370     sema: *Sema,
  25371     block: *Block,
  25372     extended: Zir.Inst.Extended.InstData,
  25373 ) CompileError!Air.Inst.Ref {
  25374     const pt = sema.pt;
  25375     const zcu = pt.zcu;
  25376     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25377     const name_src = block.builtinCallArgSrc(extra.node, 0);
  25378     const val_src = block.builtinCallArgSrc(extra.node, 1);
  25379 
  25380     const name = try sema.resolveConstString(block, name_src, extra.lhs, .{ .simple = .operand_cDefine_macro_name });
  25381     const rhs = try sema.resolveInst(extra.rhs);
  25382     if (sema.typeOf(rhs).zigTypeTag(zcu) != .void) {
  25383         const value = try sema.resolveConstString(block, val_src, extra.rhs, .{ .simple = .operand_cDefine_macro_value });
  25384         try block.c_import_buf.?.print("#define {s} {s}\n", .{ name, value });
  25385     } else {
  25386         try block.c_import_buf.?.print("#define {s}\n", .{name});
  25387     }
  25388     return .void_value;
  25389 }
  25390 
  25391 fn zirWasmMemorySize(
  25392     sema: *Sema,
  25393     block: *Block,
  25394     extended: Zir.Inst.Extended.InstData,
  25395 ) CompileError!Air.Inst.Ref {
  25396     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25397     const index_src = block.builtinCallArgSrc(extra.node, 0);
  25398     const builtin_src = block.nodeOffset(extra.node);
  25399     const target = sema.pt.zcu.getTarget();
  25400     if (!target.cpu.arch.isWasm()) {
  25401         return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  25402     }
  25403 
  25404     const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.operand, .u32, .{ .simple = .wasm_memory_index }));
  25405     try sema.requireRuntimeBlock(block, builtin_src, null);
  25406     return block.addInst(.{
  25407         .tag = .wasm_memory_size,
  25408         .data = .{ .pl_op = .{
  25409             .operand = .none,
  25410             .payload = index,
  25411         } },
  25412     });
  25413 }
  25414 
  25415 fn zirWasmMemoryGrow(
  25416     sema: *Sema,
  25417     block: *Block,
  25418     extended: Zir.Inst.Extended.InstData,
  25419 ) CompileError!Air.Inst.Ref {
  25420     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25421     const builtin_src = block.nodeOffset(extra.node);
  25422     const index_src = block.builtinCallArgSrc(extra.node, 0);
  25423     const delta_src = block.builtinCallArgSrc(extra.node, 1);
  25424     const target = sema.pt.zcu.getTarget();
  25425     if (!target.cpu.arch.isWasm()) {
  25426         return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
  25427     }
  25428 
  25429     const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.lhs, .u32, .{ .simple = .wasm_memory_index }));
  25430     const delta = try sema.coerce(block, .usize, try sema.resolveInst(extra.rhs), delta_src);
  25431 
  25432     try sema.requireRuntimeBlock(block, builtin_src, null);
  25433     return block.addInst(.{
  25434         .tag = .wasm_memory_grow,
  25435         .data = .{ .pl_op = .{
  25436             .operand = delta,
  25437             .payload = index,
  25438         } },
  25439     });
  25440 }
  25441 
  25442 fn resolvePrefetchOptions(
  25443     sema: *Sema,
  25444     block: *Block,
  25445     src: LazySrcLoc,
  25446     zir_ref: Zir.Inst.Ref,
  25447 ) CompileError!std.builtin.PrefetchOptions {
  25448     const pt = sema.pt;
  25449     const zcu = pt.zcu;
  25450     const gpa = sema.gpa;
  25451     const ip = &zcu.intern_pool;
  25452     const options_ty = try sema.getBuiltinType(src, .PrefetchOptions);
  25453     const options = try sema.coerce(block, options_ty, try sema.resolveInst(zir_ref), src);
  25454 
  25455     const rw_src = block.src(.{ .init_field_rw = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25456     const locality_src = block.src(.{ .init_field_locality = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25457     const cache_src = block.src(.{ .init_field_cache = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25458 
  25459     const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "rw", .no_embedded_nulls), rw_src);
  25460     const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{ .simple = .prefetch_options });
  25461 
  25462     const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "locality", .no_embedded_nulls), locality_src);
  25463     const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{ .simple = .prefetch_options });
  25464 
  25465     const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "cache", .no_embedded_nulls), cache_src);
  25466     const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{ .simple = .prefetch_options });
  25467 
  25468     return std.builtin.PrefetchOptions{
  25469         .rw = try sema.interpretBuiltinType(block, rw_src, rw_val, std.builtin.PrefetchOptions.Rw),
  25470         .locality = @intCast(try locality_val.toUnsignedIntSema(pt)),
  25471         .cache = try sema.interpretBuiltinType(block, cache_src, cache_val, std.builtin.PrefetchOptions.Cache),
  25472     };
  25473 }
  25474 
  25475 fn zirPrefetch(
  25476     sema: *Sema,
  25477     block: *Block,
  25478     extended: Zir.Inst.Extended.InstData,
  25479 ) CompileError!Air.Inst.Ref {
  25480     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25481     const ptr_src = block.builtinCallArgSrc(extra.node, 0);
  25482     const opts_src = block.builtinCallArgSrc(extra.node, 1);
  25483     const ptr = try sema.resolveInst(extra.lhs);
  25484     try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr));
  25485 
  25486     const options = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs);
  25487 
  25488     if (!block.isComptime()) {
  25489         _ = try block.addInst(.{
  25490             .tag = .prefetch,
  25491             .data = .{ .prefetch = .{
  25492                 .ptr = ptr,
  25493                 .rw = options.rw,
  25494                 .locality = options.locality,
  25495                 .cache = options.cache,
  25496             } },
  25497         });
  25498     }
  25499 
  25500     return .void_value;
  25501 }
  25502 
  25503 fn resolveExternOptions(
  25504     sema: *Sema,
  25505     block: *Block,
  25506     src: LazySrcLoc,
  25507     zir_ref: Zir.Inst.Ref,
  25508 ) CompileError!struct {
  25509     name: InternPool.NullTerminatedString,
  25510     library_name: InternPool.OptionalNullTerminatedString,
  25511     linkage: std.builtin.GlobalLinkage,
  25512     visibility: std.builtin.SymbolVisibility,
  25513     is_thread_local: bool,
  25514     is_dll_import: bool,
  25515     relocation: std.builtin.ExternOptions.Relocation,
  25516 } {
  25517     const pt = sema.pt;
  25518     const zcu = pt.zcu;
  25519     const gpa = sema.gpa;
  25520     const ip = &zcu.intern_pool;
  25521     const options_inst = try sema.resolveInst(zir_ref);
  25522     const extern_options_ty = try sema.getBuiltinType(src, .ExternOptions);
  25523     const options = try sema.coerce(block, extern_options_ty, options_inst, src);
  25524 
  25525     const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25526     const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25527     const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25528     const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25529     const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25530     const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25531     const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node });
  25532 
  25533     const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
  25534     const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });
  25535 
  25536     const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "library_name", .no_embedded_nulls), library_src);
  25537     const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ .simple = .extern_options });
  25538 
  25539     const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
  25540     const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options });
  25541     const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage);
  25542 
  25543     const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
  25544     const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_ref, .{ .simple = .extern_options });
  25545     const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility);
  25546 
  25547     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);
  25548     const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options });
  25549 
  25550     const library_name = if (library_name_val.optionalValue(zcu)) |library_name_payload| library_name: {
  25551         const library_name = try sema.toConstString(block, library_src, Air.internedToRef(library_name_payload.toIntern()), .{ .simple = .extern_options });
  25552         if (library_name.len == 0) {
  25553             return sema.fail(block, library_src, "library name cannot be empty", .{});
  25554         }
  25555         try sema.handleExternLibName(block, library_src, library_name);
  25556         break :library_name library_name;
  25557     } else null;
  25558 
  25559     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);
  25560     const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options });
  25561 
  25562     const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "relocation", .no_embedded_nulls), relocation_src);
  25563     const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });
  25564     const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation);
  25565 
  25566     if (name.len == 0) {
  25567         return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
  25568     }
  25569 
  25570     if (linkage != .weak and linkage != .strong) {
  25571         return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{});
  25572     }
  25573 
  25574     return .{
  25575         .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls),
  25576         .library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
  25577         .linkage = linkage,
  25578         .visibility = visibility,
  25579         .is_thread_local = is_thread_local_val.toBool(),
  25580         .is_dll_import = is_dll_import_val.toBool(),
  25581         .relocation = relocation,
  25582     };
  25583 }
  25584 
  25585 fn zirBuiltinExtern(
  25586     sema: *Sema,
  25587     block: *Block,
  25588     extended: Zir.Inst.Extended.InstData,
  25589 ) CompileError!Air.Inst.Ref {
  25590     const pt = sema.pt;
  25591     const zcu = pt.zcu;
  25592     const ip = &zcu.intern_pool;
  25593     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
  25594     const src = block.nodeOffset(extra.node);
  25595     const ty_src = block.builtinCallArgSrc(extra.node, 0);
  25596     const options_src = block.builtinCallArgSrc(extra.node, 1);
  25597 
  25598     var ty = try sema.resolveType(block, ty_src, extra.lhs);
  25599     if (!ty.isPtrAtRuntime(zcu)) {
  25600         return sema.fail(block, ty_src, "expected (optional) pointer", .{});
  25601     }
  25602     if (!try sema.validateExternType(ty, .other)) {
  25603         const msg = msg: {
  25604             const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ty.fmt(pt)});
  25605             errdefer msg.destroy(sema.gpa);
  25606             try sema.explainWhyTypeIsNotExtern(msg, ty_src, ty, .other);
  25607             break :msg msg;
  25608         };
  25609         return sema.failWithOwnedErrorMsg(block, msg);
  25610     }
  25611 
  25612     const options = try sema.resolveExternOptions(block, options_src, extra.rhs);
  25613     switch (options.linkage) {
  25614         .internal => if (options.visibility != .default) {
  25615             return sema.fail(block, options_src, "internal symbol cannot have non-default visibility", .{});
  25616         },
  25617         .strong, .weak => {},
  25618         .link_once => return sema.fail(block, options_src, "external symbol cannot have link once linkage", .{}),
  25619     }
  25620     switch (options.relocation) {
  25621         .any => {},
  25622         .pcrel => if (options.visibility == .default) return sema.fail(block, options_src, "cannot require a pc-relative relocation to a symbol with default visibility", .{}),
  25623     }
  25624 
  25625     // TODO: error for threadlocal functions, non-const functions, etc
  25626 
  25627     if (options.linkage == .weak and !ty.ptrAllowsZero(zcu)) {
  25628         ty = try pt.optionalType(ty.toIntern());
  25629     }
  25630     const ptr_info = ty.ptrInfo(zcu);
  25631 
  25632     const extern_val = try pt.getExtern(.{
  25633         .name = options.name,
  25634         .ty = ptr_info.child,
  25635         .lib_name = options.library_name,
  25636         .linkage = options.linkage,
  25637         .visibility = options.visibility,
  25638         .is_threadlocal = options.is_thread_local,
  25639         .is_dll_import = options.is_dll_import,
  25640         .relocation = options.relocation,
  25641         .is_const = ptr_info.flags.is_const,
  25642         .alignment = ptr_info.flags.alignment,
  25643         .@"addrspace" = ptr_info.flags.address_space,
  25644         // This instruction is just for source locations.
  25645         // `builtin_extern` doesn't provide enough information, and isn't currently tracked.
  25646         // So, for now, just use our containing `declaration`.
  25647         .zir_index = switch (sema.owner.unwrap()) {
  25648             .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,
  25649             .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?,
  25650             .memoized_state => unreachable,
  25651             .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
  25652             .func => |func| zir_index: {
  25653                 const func_info = zcu.funcInfo(func);
  25654                 const owner_func_info = if (func_info.generic_owner != .none) owner: {
  25655                     break :owner zcu.funcInfo(func_info.generic_owner);
  25656                 } else func_info;
  25657                 break :zir_index ip.getNav(owner_func_info.owner_nav).analysis.?.zir_index;
  25658             },
  25659         },
  25660         .owner_nav = undefined, // ignored by `getExtern`
  25661     });
  25662 
  25663     const uncasted_ptr = try sema.analyzeNavRef(block, src, ip.indexToKey(extern_val).@"extern".owner_nav);
  25664     // We want to cast to `ty`, but that isn't necessarily an allowed coercion.
  25665     if (try sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| {
  25666         const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, ty);
  25667         return Air.internedToRef(casted_ptr_val.toIntern());
  25668     } else {
  25669         return block.addBitCast(ty, uncasted_ptr);
  25670     }
  25671 }
  25672 
  25673 fn zirWorkItem(
  25674     sema: *Sema,
  25675     block: *Block,
  25676     extended: Zir.Inst.Extended.InstData,
  25677     zir_tag: Zir.Inst.Extended,
  25678 ) CompileError!Air.Inst.Ref {
  25679     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25680     const dimension_src = block.builtinCallArgSrc(extra.node, 0);
  25681     const builtin_src = block.nodeOffset(extra.node);
  25682     const target = sema.pt.zcu.getTarget();
  25683 
  25684     switch (target.cpu.arch) {
  25685         // TODO: Allow for other GPU targets.
  25686         .amdgcn, .spirv64, .spirv32, .nvptx, .nvptx64 => {},
  25687         else => {
  25688             return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)});
  25689         },
  25690     }
  25691 
  25692     const dimension: u32 = @intCast(try sema.resolveInt(block, dimension_src, extra.operand, .u32, .{ .simple = .work_group_dim_index }));
  25693     try sema.requireRuntimeBlock(block, builtin_src, null);
  25694 
  25695     return block.addInst(.{
  25696         .tag = switch (zir_tag) {
  25697             .work_item_id => .work_item_id,
  25698             .work_group_size => .work_group_size,
  25699             .work_group_id => .work_group_id,
  25700             else => unreachable,
  25701         },
  25702         .data = .{ .pl_op = .{
  25703             .operand = .none,
  25704             .payload = dimension,
  25705         } },
  25706     });
  25707 }
  25708 
  25709 fn zirInComptime(
  25710     sema: *Sema,
  25711     block: *Block,
  25712 ) CompileError!Air.Inst.Ref {
  25713     _ = sema;
  25714     return if (block.isComptime()) .bool_true else .bool_false;
  25715 }
  25716 
  25717 fn zirBuiltinValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  25718     const pt = sema.pt;
  25719     const zcu = pt.zcu;
  25720     const gpa = zcu.gpa;
  25721     const ip = &zcu.intern_pool;
  25722 
  25723     const src_node: std.zig.Ast.Node.Offset = @enumFromInt(@as(i32, @bitCast(extended.operand)));
  25724     const src = block.nodeOffset(src_node);
  25725     const value: Zir.Inst.BuiltinValue = @enumFromInt(extended.small);
  25726 
  25727     const ty = switch (value) {
  25728         // zig fmt: off
  25729         .atomic_order       => try sema.getBuiltinType(src, .AtomicOrder),
  25730         .atomic_rmw_op      => try sema.getBuiltinType(src, .AtomicRmwOp),
  25731         .calling_convention => try sema.getBuiltinType(src, .CallingConvention),
  25732         .address_space      => try sema.getBuiltinType(src, .AddressSpace),
  25733         .float_mode         => try sema.getBuiltinType(src, .FloatMode),
  25734         .reduce_op          => try sema.getBuiltinType(src, .ReduceOp),
  25735         .call_modifier      => try sema.getBuiltinType(src, .CallModifier),
  25736         .prefetch_options   => try sema.getBuiltinType(src, .PrefetchOptions),
  25737         .export_options     => try sema.getBuiltinType(src, .ExportOptions),
  25738         .extern_options     => try sema.getBuiltinType(src, .ExternOptions),
  25739         .type_info          => try sema.getBuiltinType(src, .Type),
  25740         .branch_hint        => try sema.getBuiltinType(src, .BranchHint),
  25741         .clobbers           => try sema.getBuiltinType(src, .@"assembly.Clobbers"),
  25742         // zig fmt: on
  25743 
  25744         // Values are handled here.
  25745         .calling_convention_c => {
  25746             const callconv_ty = try sema.getBuiltinType(src, .CallingConvention);
  25747             return try sema.namespaceLookupVal(
  25748                 block,
  25749                 src,
  25750                 callconv_ty.getNamespaceIndex(zcu),
  25751                 try ip.getOrPutString(gpa, pt.tid, "c", .no_embedded_nulls),
  25752             ) orelse @panic("std.builtin is corrupt");
  25753         },
  25754         .calling_convention_inline => {
  25755             comptime assert(@typeInfo(std.builtin.CallingConvention.Tag).@"enum".tag_type == u8);
  25756             const callconv_ty = try sema.getBuiltinType(src, .CallingConvention);
  25757             const callconv_tag_ty = callconv_ty.unionTagType(zcu) orelse @panic("std.builtin is corrupt");
  25758             const inline_tag_val = try pt.enumValue(
  25759                 callconv_tag_ty,
  25760                 (try pt.intValue(
  25761                     .u8,
  25762                     @intFromEnum(std.builtin.CallingConvention.@"inline"),
  25763                 )).toIntern(),
  25764             );
  25765             return sema.coerce(block, callconv_ty, Air.internedToRef(inline_tag_val.toIntern()), src);
  25766         },
  25767     };
  25768     return Air.internedToRef(ty.toIntern());
  25769 }
  25770 
  25771 fn zirInplaceArithResultTy(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
  25772     const pt = sema.pt;
  25773     const zcu = pt.zcu;
  25774 
  25775     const lhs = try sema.resolveInst(@enumFromInt(extended.operand));
  25776     const lhs_ty = sema.typeOf(lhs);
  25777 
  25778     const op: Zir.Inst.InplaceOp = @enumFromInt(extended.small);
  25779     const ty: Type = switch (op) {
  25780         .add_eq => ty: {
  25781             const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty;
  25782             switch (ptr_size) {
  25783                 .one, .slice => break :ty lhs_ty, // invalid, let it error
  25784                 .many, .c => break :ty .usize, // `[*]T + usize`
  25785             }
  25786         },
  25787         .sub_eq => ty: {
  25788             const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty;
  25789             switch (ptr_size) {
  25790                 .one, .slice => break :ty lhs_ty, // invalid, let it error
  25791                 .many, .c => break :ty .generic_poison, // could be `[*]T - [*]T` or `[*]T - usize`
  25792             }
  25793         },
  25794     };
  25795     return Air.internedToRef(ty.toIntern());
  25796 }
  25797 
  25798 fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
  25799     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
  25800     const uncoerced_hint = try sema.resolveInst(extra.operand);
  25801     const operand_src = block.builtinCallArgSrc(extra.node, 0);
  25802 
  25803     const hint_ty = try sema.getBuiltinType(operand_src, .BranchHint);
  25804     const coerced_hint = try sema.coerce(block, hint_ty, uncoerced_hint, operand_src);
  25805     const hint_val = try sema.resolveConstDefinedValue(block, operand_src, coerced_hint, .{ .simple = .operand_branchHint });
  25806 
  25807     // We only apply the first hint in a branch.
  25808     // This allows user-provided hints to override implicit cold hints.
  25809     if (sema.branch_hint == null) {
  25810         sema.branch_hint = try sema.interpretBuiltinType(block, operand_src, hint_val, std.builtin.BranchHint);
  25811     }
  25812 }
  25813 
  25814 fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
  25815     if (block.isComptime()) {
  25816         const msg, const fail_block = msg: {
  25817             const msg = try sema.errMsg(src, "unable to evaluate comptime expression", .{});
  25818             errdefer msg.destroy(sema.gpa);
  25819 
  25820             if (runtime_src) |some| {
  25821                 try sema.errNote(some, msg, "operation is runtime due to this operand", .{});
  25822             }
  25823 
  25824             const fail_block = try block.explainWhyBlockIsComptime(msg);
  25825 
  25826             break :msg .{ msg, fail_block };
  25827         };
  25828         return sema.failWithOwnedErrorMsg(fail_block, msg);
  25829     }
  25830 }
  25831 
  25832 /// Emit a compile error if type cannot be used for a runtime variable.
  25833 pub fn validateVarType(
  25834     sema: *Sema,
  25835     block: *Block,
  25836     src: LazySrcLoc,
  25837     var_ty: Type,
  25838     is_extern: bool,
  25839 ) CompileError!void {
  25840     const pt = sema.pt;
  25841     const zcu = pt.zcu;
  25842     if (is_extern) {
  25843         if (!try sema.validateExternType(var_ty, .other)) {
  25844             const msg = msg: {
  25845                 const msg = try sema.errMsg(src, "extern variable cannot have type '{f}'", .{var_ty.fmt(pt)});
  25846                 errdefer msg.destroy(sema.gpa);
  25847                 try sema.explainWhyTypeIsNotExtern(msg, src, var_ty, .other);
  25848                 break :msg msg;
  25849             };
  25850             return sema.failWithOwnedErrorMsg(block, msg);
  25851         }
  25852     } else {
  25853         if (var_ty.zigTypeTag(zcu) == .@"opaque") {
  25854             return sema.fail(
  25855                 block,
  25856                 src,
  25857                 "non-extern variable with opaque type '{f}'",
  25858                 .{var_ty.fmt(pt)},
  25859             );
  25860         }
  25861     }
  25862 
  25863     if (!try var_ty.comptimeOnlySema(pt)) return;
  25864 
  25865     const msg = msg: {
  25866         const msg = try sema.errMsg(src, "variable of type '{f}' must be const or comptime", .{var_ty.fmt(pt)});
  25867         errdefer msg.destroy(sema.gpa);
  25868 
  25869         try sema.explainWhyTypeIsComptime(msg, src, var_ty);
  25870         if (var_ty.zigTypeTag(zcu) == .comptime_int or var_ty.zigTypeTag(zcu) == .comptime_float) {
  25871             try sema.errNote(src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{});
  25872         }
  25873 
  25874         break :msg msg;
  25875     };
  25876     return sema.failWithOwnedErrorMsg(block, msg);
  25877 }
  25878 
  25879 const TypeSet = std.AutoHashMapUnmanaged(InternPool.Index, void);
  25880 
  25881 fn explainWhyTypeIsComptime(
  25882     sema: *Sema,
  25883     msg: *Zcu.ErrorMsg,
  25884     src_loc: LazySrcLoc,
  25885     ty: Type,
  25886 ) CompileError!void {
  25887     var type_set = TypeSet{};
  25888     defer type_set.deinit(sema.gpa);
  25889 
  25890     try ty.resolveFully(sema.pt);
  25891     return sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty, &type_set);
  25892 }
  25893 
  25894 fn explainWhyTypeIsComptimeInner(
  25895     sema: *Sema,
  25896     msg: *Zcu.ErrorMsg,
  25897     src_loc: LazySrcLoc,
  25898     ty: Type,
  25899     type_set: *TypeSet,
  25900 ) CompileError!void {
  25901     const pt = sema.pt;
  25902     const zcu = pt.zcu;
  25903     const ip = &zcu.intern_pool;
  25904     switch (ty.zigTypeTag(zcu)) {
  25905         .bool,
  25906         .int,
  25907         .float,
  25908         .error_set,
  25909         .@"enum",
  25910         .frame,
  25911         .@"anyframe",
  25912         .void,
  25913         => return,
  25914 
  25915         .@"fn" => {
  25916             try sema.errNote(src_loc, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)});
  25917         },
  25918 
  25919         .type => {
  25920             try sema.errNote(src_loc, msg, "types are not available at runtime", .{});
  25921         },
  25922 
  25923         .comptime_float,
  25924         .comptime_int,
  25925         .enum_literal,
  25926         .noreturn,
  25927         .undefined,
  25928         .null,
  25929         => return,
  25930 
  25931         .@"opaque" => {
  25932             try sema.errNote(src_loc, msg, "opaque type '{f}' has undefined size", .{ty.fmt(pt)});
  25933         },
  25934 
  25935         .array, .vector => {
  25936             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set);
  25937         },
  25938         .pointer => {
  25939             const elem_ty = ty.elemType2(zcu);
  25940             if (elem_ty.zigTypeTag(zcu) == .@"fn") {
  25941                 const fn_info = zcu.typeToFunc(elem_ty).?;
  25942                 if (fn_info.is_generic) {
  25943                     try sema.errNote(src_loc, msg, "function is generic", .{});
  25944                 }
  25945                 switch (fn_info.cc) {
  25946                     .@"inline" => try sema.errNote(src_loc, msg, "function has inline calling convention", .{}),
  25947                     else => {},
  25948                 }
  25949                 if (Type.fromInterned(fn_info.return_type).comptimeOnly(zcu)) {
  25950                     try sema.errNote(src_loc, msg, "function has a comptime-only return type", .{});
  25951                 }
  25952                 return;
  25953             }
  25954             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.childType(zcu), type_set);
  25955         },
  25956 
  25957         .optional => {
  25958             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.optionalChild(zcu), type_set);
  25959         },
  25960         .error_union => {
  25961             try sema.explainWhyTypeIsComptimeInner(msg, src_loc, ty.errorUnionPayload(zcu), type_set);
  25962         },
  25963 
  25964         .@"struct" => {
  25965             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  25966 
  25967             if (zcu.typeToStruct(ty)) |struct_type| {
  25968                 for (0..struct_type.field_types.len) |i| {
  25969                     const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  25970                     const field_src: LazySrcLoc = .{
  25971                         .base_node_inst = struct_type.zir_index,
  25972                         .offset = .{ .container_field_type = @intCast(i) },
  25973                     };
  25974 
  25975                     if (try field_ty.comptimeOnlySema(pt)) {
  25976                         try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{});
  25977                         try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set);
  25978                     }
  25979                 }
  25980             }
  25981             // TODO tuples
  25982         },
  25983 
  25984         .@"union" => {
  25985             if ((try type_set.getOrPut(sema.gpa, ty.toIntern())).found_existing) return;
  25986 
  25987             if (zcu.typeToUnion(ty)) |union_obj| {
  25988                 for (0..union_obj.field_types.len) |i| {
  25989                     const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]);
  25990                     const field_src: LazySrcLoc = .{
  25991                         .base_node_inst = union_obj.zir_index,
  25992                         .offset = .{ .container_field_type = @intCast(i) },
  25993                     };
  25994 
  25995                     if (try field_ty.comptimeOnlySema(pt)) {
  25996                         try sema.errNote(field_src, msg, "union requires comptime because of this field", .{});
  25997                         try sema.explainWhyTypeIsComptimeInner(msg, field_src, field_ty, type_set);
  25998                     }
  25999                 }
  26000             }
  26001         },
  26002     }
  26003 }
  26004 
  26005 const ExternPosition = enum {
  26006     ret_ty,
  26007     param_ty,
  26008     union_field,
  26009     struct_field,
  26010     element,
  26011     other,
  26012 };
  26013 
  26014 /// Returns true if `ty` is allowed in extern types.
  26015 /// Does *NOT* require `ty` to be resolved in any way.
  26016 /// Calls `resolveLayout` for packed containers.
  26017 fn validateExternType(
  26018     sema: *Sema,
  26019     ty: Type,
  26020     position: ExternPosition,
  26021 ) !bool {
  26022     const pt = sema.pt;
  26023     const zcu = pt.zcu;
  26024     switch (ty.zigTypeTag(zcu)) {
  26025         .type,
  26026         .comptime_float,
  26027         .comptime_int,
  26028         .enum_literal,
  26029         .undefined,
  26030         .null,
  26031         .error_union,
  26032         .error_set,
  26033         .frame,
  26034         => return false,
  26035         .void => return position == .union_field or position == .ret_ty or position == .struct_field or position == .element,
  26036         .noreturn => return position == .ret_ty,
  26037         .@"opaque",
  26038         .bool,
  26039         .float,
  26040         .@"anyframe",
  26041         => return true,
  26042         .pointer => {
  26043             if (ty.childType(zcu).zigTypeTag(zcu) == .@"fn") {
  26044                 return ty.isConstPtr(zcu) and try sema.validateExternType(ty.childType(zcu), .other);
  26045             }
  26046             return !(ty.isSlice(zcu) or try ty.comptimeOnlySema(pt));
  26047         },
  26048         .int => switch (ty.intInfo(zcu).bits) {
  26049             0, 8, 16, 32, 64, 128 => return true,
  26050             else => return false,
  26051         },
  26052         .@"fn" => {
  26053             if (position != .other) return false;
  26054             // For now we want to authorize PTX kernel to use zig objects, even if we end up exposing the ABI.
  26055             // The goal is to experiment with more integrated CPU/GPU code.
  26056             if (ty.fnCallingConvention(zcu) == .nvptx_kernel) {
  26057                 return true;
  26058             }
  26059             return !target_util.fnCallConvAllowsZigTypes(ty.fnCallingConvention(zcu));
  26060         },
  26061         .@"enum" => {
  26062             return sema.validateExternType(ty.intTagType(zcu), position);
  26063         },
  26064         .@"struct", .@"union" => switch (ty.containerLayout(zcu)) {
  26065             .@"extern" => return true,
  26066             .@"packed" => {
  26067                 const bit_size = try ty.bitSizeSema(pt);
  26068                 switch (bit_size) {
  26069                     0, 8, 16, 32, 64, 128 => return true,
  26070                     else => return false,
  26071                 }
  26072             },
  26073             .auto => return !(try ty.hasRuntimeBitsSema(pt)),
  26074         },
  26075         .array => {
  26076             if (position == .ret_ty or position == .param_ty) return false;
  26077             return sema.validateExternType(ty.elemType2(zcu), .element);
  26078         },
  26079         .vector => return sema.validateExternType(ty.elemType2(zcu), .element),
  26080         .optional => return ty.isPtrLikeOptional(zcu),
  26081     }
  26082 }
  26083 
  26084 fn explainWhyTypeIsNotExtern(
  26085     sema: *Sema,
  26086     msg: *Zcu.ErrorMsg,
  26087     src_loc: LazySrcLoc,
  26088     ty: Type,
  26089     position: ExternPosition,
  26090 ) CompileError!void {
  26091     const pt = sema.pt;
  26092     const zcu = pt.zcu;
  26093     switch (ty.zigTypeTag(zcu)) {
  26094         .@"opaque",
  26095         .bool,
  26096         .float,
  26097         .@"anyframe",
  26098         => return,
  26099 
  26100         .type,
  26101         .comptime_float,
  26102         .comptime_int,
  26103         .enum_literal,
  26104         .undefined,
  26105         .null,
  26106         .error_union,
  26107         .error_set,
  26108         .frame,
  26109         => return,
  26110 
  26111         .pointer => {
  26112             if (ty.isSlice(zcu)) {
  26113                 try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{});
  26114             } else {
  26115                 const pointee_ty = ty.childType(zcu);
  26116                 if (!ty.isConstPtr(zcu) and pointee_ty.zigTypeTag(zcu) == .@"fn") {
  26117                     try sema.errNote(src_loc, msg, "pointer to extern function must be 'const'", .{});
  26118                 } else if (try ty.comptimeOnlySema(pt)) {
  26119                     try sema.errNote(src_loc, msg, "pointer to comptime-only type '{f}'", .{pointee_ty.fmt(pt)});
  26120                     try sema.explainWhyTypeIsComptime(msg, src_loc, ty);
  26121                 }
  26122                 try sema.explainWhyTypeIsNotExtern(msg, src_loc, pointee_ty, .other);
  26123             }
  26124         },
  26125         .void => try sema.errNote(src_loc, msg, "'void' is a zero bit type; for C 'void' use 'anyopaque'", .{}),
  26126         .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
  26127         .int => if (!std.math.isPowerOfTwo(ty.intInfo(zcu).bits)) {
  26128             try sema.errNote(src_loc, msg, "only integers with 0 or power of two bits are extern compatible", .{});
  26129         } else {
  26130             try sema.errNote(src_loc, msg, "only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible", .{});
  26131         },
  26132         .@"fn" => {
  26133             if (position != .other) {
  26134                 try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  26135                 try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  26136                 return;
  26137             }
  26138             switch (ty.fnCallingConvention(zcu)) {
  26139                 .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}),
  26140                 .async => try sema.errNote(src_loc, msg, "async function cannot be extern", .{}),
  26141                 .@"inline" => try sema.errNote(src_loc, msg, "inline function cannot be extern", .{}),
  26142                 else => return,
  26143             }
  26144         },
  26145         .@"enum" => {
  26146             const tag_ty = ty.intTagType(zcu);
  26147             try sema.errNote(src_loc, msg, "enum tag type '{f}' is not extern compatible", .{tag_ty.fmt(pt)});
  26148             try sema.explainWhyTypeIsNotExtern(msg, src_loc, tag_ty, position);
  26149         },
  26150         .@"struct" => try sema.errNote(src_loc, msg, "only extern structs and ABI sized packed structs are extern compatible", .{}),
  26151         .@"union" => try sema.errNote(src_loc, msg, "only extern unions and ABI sized packed unions are extern compatible", .{}),
  26152         .array => {
  26153             if (position == .ret_ty) {
  26154                 return sema.errNote(src_loc, msg, "arrays are not allowed as a return type", .{});
  26155             } else if (position == .param_ty) {
  26156                 return sema.errNote(src_loc, msg, "arrays are not allowed as a parameter type", .{});
  26157             }
  26158             try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element);
  26159         },
  26160         .vector => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.elemType2(zcu), .element),
  26161         .optional => try sema.errNote(src_loc, msg, "only pointer like optionals are extern compatible", .{}),
  26162     }
  26163 }
  26164 
  26165 /// Returns true if `ty` is allowed in packed types.
  26166 /// Does not require `ty` to be resolved in any way, but may resolve whether it is comptime-only.
  26167 fn validatePackedType(sema: *Sema, ty: Type) !bool {
  26168     const pt = sema.pt;
  26169     const zcu = pt.zcu;
  26170     return switch (ty.zigTypeTag(zcu)) {
  26171         .type,
  26172         .comptime_float,
  26173         .comptime_int,
  26174         .enum_literal,
  26175         .undefined,
  26176         .null,
  26177         .error_union,
  26178         .error_set,
  26179         .frame,
  26180         .noreturn,
  26181         .@"opaque",
  26182         .@"anyframe",
  26183         .@"fn",
  26184         .array,
  26185         => false,
  26186         .optional => return ty.isPtrLikeOptional(zcu),
  26187         .void,
  26188         .bool,
  26189         .float,
  26190         .int,
  26191         .vector,
  26192         => true,
  26193         .@"enum" => switch (zcu.intern_pool.loadEnumType(ty.toIntern()).tag_mode) {
  26194             .auto => false,
  26195             .explicit, .nonexhaustive => true,
  26196         },
  26197         .pointer => !ty.isSlice(zcu) and !try ty.comptimeOnlySema(pt),
  26198         .@"struct", .@"union" => ty.containerLayout(zcu) == .@"packed",
  26199     };
  26200 }
  26201 
  26202 fn explainWhyTypeIsNotPacked(
  26203     sema: *Sema,
  26204     msg: *Zcu.ErrorMsg,
  26205     src_loc: LazySrcLoc,
  26206     ty: Type,
  26207 ) CompileError!void {
  26208     const pt = sema.pt;
  26209     const zcu = pt.zcu;
  26210     switch (ty.zigTypeTag(zcu)) {
  26211         .void,
  26212         .bool,
  26213         .float,
  26214         .int,
  26215         .vector,
  26216         .@"enum",
  26217         => return,
  26218         .type,
  26219         .comptime_float,
  26220         .comptime_int,
  26221         .enum_literal,
  26222         .undefined,
  26223         .null,
  26224         .frame,
  26225         .noreturn,
  26226         .@"opaque",
  26227         .error_union,
  26228         .error_set,
  26229         .@"anyframe",
  26230         .optional,
  26231         .array,
  26232         => try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}),
  26233         .pointer => if (ty.isSlice(zcu)) {
  26234             try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{});
  26235         } else {
  26236             try sema.errNote(src_loc, msg, "comptime-only pointer has no guaranteed in-memory representation", .{});
  26237             try sema.explainWhyTypeIsComptime(msg, src_loc, ty);
  26238         },
  26239         .@"fn" => {
  26240             try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{});
  26241             try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{});
  26242         },
  26243         .@"struct" => try sema.errNote(src_loc, msg, "only packed structs layout are allowed in packed types", .{}),
  26244         .@"union" => try sema.errNote(src_loc, msg, "only packed unions layout are allowed in packed types", .{}),
  26245     }
  26246 }
  26247 
  26248 /// Backends depend on panic decls being available when lowering safety-checked
  26249 /// instructions. This function ensures the panic function will be available to
  26250 /// be called during that time.
  26251 fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !void {
  26252     // If the backend doesn't support `.panic_fn`, it doesn't want us to lower the panic handlers.
  26253     // The backend will transform panics into traps instead.
  26254     if (sema.pt.zcu.backendSupportsFeature(.panic_fn)) {
  26255         _ = try sema.getPanicIdFunc(src, panic_id);
  26256     }
  26257 }
  26258 
  26259 fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index {
  26260     const zcu = sema.pt.zcu;
  26261     try sema.ensureMemoizedStateResolved(src, .panic);
  26262     const panic_func = zcu.builtin_decl_values.get(panic_id.toBuiltin());
  26263     try zcu.ensureFuncBodyAnalysisQueued(panic_func);
  26264     switch (sema.owner.unwrap()) {
  26265         .@"comptime", .nav_ty, .nav_val, .type, .memoized_state => {},
  26266         .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(owner_func, true),
  26267     }
  26268     return panic_func;
  26269 }
  26270 
  26271 fn addSafetyCheck(
  26272     sema: *Sema,
  26273     parent_block: *Block,
  26274     src: LazySrcLoc,
  26275     ok: Air.Inst.Ref,
  26276     panic_id: Zcu.SimplePanicId,
  26277 ) !void {
  26278     const gpa = sema.gpa;
  26279     assert(!parent_block.isComptime());
  26280 
  26281     var fail_block: Block = .{
  26282         .parent = parent_block,
  26283         .sema = sema,
  26284         .namespace = parent_block.namespace,
  26285         .instructions = .{},
  26286         .inlining = parent_block.inlining,
  26287         .comptime_reason = null,
  26288         .src_base_inst = parent_block.src_base_inst,
  26289         .type_name_ctx = parent_block.type_name_ctx,
  26290     };
  26291 
  26292     defer fail_block.instructions.deinit(gpa);
  26293 
  26294     try sema.safetyPanic(&fail_block, src, panic_id);
  26295     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  26296 }
  26297 
  26298 fn addSafetyCheckExtra(
  26299     sema: *Sema,
  26300     parent_block: *Block,
  26301     ok: Air.Inst.Ref,
  26302     fail_block: *Block,
  26303 ) !void {
  26304     const gpa = sema.gpa;
  26305 
  26306     try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
  26307 
  26308     try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len +
  26309         1 + // The main block only needs space for the cond_br.
  26310         @typeInfo(Air.CondBr).@"struct".fields.len +
  26311         1 + // The ok branch of the cond_br only needs space for the br.
  26312         fail_block.instructions.items.len);
  26313 
  26314     try sema.air_instructions.ensureUnusedCapacity(gpa, 3);
  26315     const block_inst: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
  26316     const cond_br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(block_inst) + 1);
  26317     const br_inst: Air.Inst.Index = @enumFromInt(@intFromEnum(cond_br_inst) + 1);
  26318     sema.air_instructions.appendAssumeCapacity(.{
  26319         .tag = .block,
  26320         .data = .{ .ty_pl = .{
  26321             .ty = .void_type,
  26322             .payload = sema.addExtraAssumeCapacity(Air.Block{
  26323                 .body_len = 1,
  26324             }),
  26325         } },
  26326     });
  26327     sema.air_extra.appendAssumeCapacity(@intFromEnum(cond_br_inst));
  26328 
  26329     sema.air_instructions.appendAssumeCapacity(.{
  26330         .tag = .cond_br,
  26331         .data = .{
  26332             .pl_op = .{
  26333                 .operand = ok,
  26334                 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
  26335                     .then_body_len = 1,
  26336                     .else_body_len = @intCast(fail_block.instructions.items.len),
  26337                     .branch_hints = .{
  26338                         // Safety check failure branch is cold.
  26339                         .true = .likely,
  26340                         .false = .cold,
  26341                         // Code coverage not wanted for panic branches.
  26342                         .then_cov = .none,
  26343                         .else_cov = .none,
  26344                     },
  26345                 }),
  26346             },
  26347         },
  26348     });
  26349     sema.air_extra.appendAssumeCapacity(@intFromEnum(br_inst));
  26350     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(fail_block.instructions.items));
  26351 
  26352     sema.air_instructions.appendAssumeCapacity(.{
  26353         .tag = .br,
  26354         .data = .{ .br = .{
  26355             .block_inst = block_inst,
  26356             .operand = .void_value,
  26357         } },
  26358     });
  26359 
  26360     parent_block.instructions.appendAssumeCapacity(block_inst);
  26361 }
  26362 
  26363 fn addSafetyCheckUnwrapError(
  26364     sema: *Sema,
  26365     parent_block: *Block,
  26366     src: LazySrcLoc,
  26367     operand: Air.Inst.Ref,
  26368     unwrap_err_tag: Air.Inst.Tag,
  26369     is_non_err_tag: Air.Inst.Tag,
  26370 ) !void {
  26371     assert(!parent_block.isComptime());
  26372     const ok = try parent_block.addUnOp(is_non_err_tag, operand);
  26373     const gpa = sema.gpa;
  26374 
  26375     var fail_block: Block = .{
  26376         .parent = parent_block,
  26377         .sema = sema,
  26378         .namespace = parent_block.namespace,
  26379         .instructions = .{},
  26380         .inlining = parent_block.inlining,
  26381         .comptime_reason = null,
  26382         .src_base_inst = parent_block.src_base_inst,
  26383         .type_name_ctx = parent_block.type_name_ctx,
  26384     };
  26385 
  26386     defer fail_block.instructions.deinit(gpa);
  26387 
  26388     const err = try fail_block.addTyOp(unwrap_err_tag, .anyerror, operand);
  26389     try safetyPanicUnwrapError(sema, &fail_block, src, err);
  26390 
  26391     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  26392 }
  26393 
  26394 fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air.Inst.Ref) !void {
  26395     const pt = sema.pt;
  26396     const zcu = pt.zcu;
  26397     if (!zcu.backendSupportsFeature(.panic_fn)) {
  26398         _ = try block.addNoOp(.trap);
  26399     } else {
  26400         const panic_fn = try getBuiltin(sema, src, .@"panic.unwrapError");
  26401         try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{err}, .@"safety check");
  26402     }
  26403 }
  26404 
  26405 fn addSafetyCheckIndexOob(
  26406     sema: *Sema,
  26407     parent_block: *Block,
  26408     src: LazySrcLoc,
  26409     index: Air.Inst.Ref,
  26410     len: Air.Inst.Ref,
  26411     cmp_op: Air.Inst.Tag,
  26412 ) !void {
  26413     assert(!parent_block.isComptime());
  26414     const ok = try parent_block.addBinOp(cmp_op, index, len);
  26415     return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.outOfBounds", &.{ index, len });
  26416 }
  26417 
  26418 fn addSafetyCheckInactiveUnionField(
  26419     sema: *Sema,
  26420     parent_block: *Block,
  26421     src: LazySrcLoc,
  26422     active_tag: Air.Inst.Ref,
  26423     wanted_tag: Air.Inst.Ref,
  26424 ) !void {
  26425     assert(!parent_block.isComptime());
  26426     const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
  26427     return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.inactiveUnionField", &.{ active_tag, wanted_tag });
  26428 }
  26429 
  26430 fn addSafetyCheckSentinelMismatch(
  26431     sema: *Sema,
  26432     parent_block: *Block,
  26433     src: LazySrcLoc,
  26434     maybe_sentinel: ?Value,
  26435     sentinel_ty: Type,
  26436     ptr: Air.Inst.Ref,
  26437     sentinel_index: Air.Inst.Ref,
  26438 ) !void {
  26439     assert(!parent_block.isComptime());
  26440     const pt = sema.pt;
  26441     const zcu = pt.zcu;
  26442     const expected_sentinel_val = maybe_sentinel orelse return;
  26443     const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern());
  26444 
  26445     const ptr_ty = sema.typeOf(ptr);
  26446     const actual_sentinel = if (ptr_ty.isSlice(zcu))
  26447         try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
  26448     else blk: {
  26449         const elem_ptr_ty = try ptr_ty.elemPtrType(null, pt);
  26450         const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
  26451         break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
  26452     };
  26453 
  26454     const ok = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: {
  26455         const eql = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq);
  26456         break :ok try parent_block.addReduce(eql, .And);
  26457     } else ok: {
  26458         assert(sentinel_ty.isSelfComparable(zcu, true));
  26459         break :ok try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
  26460     };
  26461 
  26462     return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{
  26463         expected_sentinel, actual_sentinel,
  26464     });
  26465 }
  26466 
  26467 fn addSafetyCheckCall(
  26468     sema: *Sema,
  26469     parent_block: *Block,
  26470     src: LazySrcLoc,
  26471     ok: Air.Inst.Ref,
  26472     comptime func_decl: Zcu.BuiltinDecl,
  26473     args: []const Air.Inst.Ref,
  26474 ) !void {
  26475     assert(!parent_block.isComptime());
  26476     const gpa = sema.gpa;
  26477     const pt = sema.pt;
  26478     const zcu = pt.zcu;
  26479 
  26480     var fail_block: Block = .{
  26481         .parent = parent_block,
  26482         .sema = sema,
  26483         .namespace = parent_block.namespace,
  26484         .instructions = .{},
  26485         .inlining = parent_block.inlining,
  26486         .comptime_reason = null,
  26487         .src_base_inst = parent_block.src_base_inst,
  26488         .type_name_ctx = parent_block.type_name_ctx,
  26489     };
  26490 
  26491     defer fail_block.instructions.deinit(gpa);
  26492 
  26493     if (!zcu.backendSupportsFeature(.panic_fn)) {
  26494         _ = try fail_block.addNoOp(.trap);
  26495     } else {
  26496         const panic_fn = try getBuiltin(sema, src, func_decl);
  26497         try sema.callBuiltin(&fail_block, src, Air.internedToRef(panic_fn), .auto, args, .@"safety check");
  26498     }
  26499 
  26500     try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
  26501 }
  26502 
  26503 /// This does not set `sema.branch_hint`.
  26504 fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) CompileError!void {
  26505     if (!sema.pt.zcu.backendSupportsFeature(.panic_fn)) {
  26506         _ = try block.addNoOp(.trap);
  26507     } else {
  26508         const panic_fn = try sema.getPanicIdFunc(src, panic_id);
  26509         try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{}, .@"safety check");
  26510     }
  26511 }
  26512 
  26513 fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
  26514     sema.branch_count += 1;
  26515     if (sema.branch_count > sema.branch_quota) {
  26516         const msg = try sema.errMsg(
  26517             src,
  26518             "evaluation exceeded {d} backwards branches",
  26519             .{sema.branch_quota},
  26520         );
  26521         try sema.errNote(
  26522             src,
  26523             msg,
  26524             "use @setEvalBranchQuota() to raise the branch limit from {d}",
  26525             .{sema.branch_quota},
  26526         );
  26527         return sema.failWithOwnedErrorMsg(block, msg);
  26528     }
  26529 }
  26530 
  26531 fn fieldVal(
  26532     sema: *Sema,
  26533     block: *Block,
  26534     src: LazySrcLoc,
  26535     object: Air.Inst.Ref,
  26536     field_name: InternPool.NullTerminatedString,
  26537     field_name_src: LazySrcLoc,
  26538 ) CompileError!Air.Inst.Ref {
  26539     // When editing this function, note that there is corresponding logic to be edited
  26540     // in `fieldPtr`. This function takes a value and returns a value.
  26541 
  26542     const pt = sema.pt;
  26543     const zcu = pt.zcu;
  26544     const ip = &zcu.intern_pool;
  26545     const object_src = src; // TODO better source location
  26546     const object_ty = sema.typeOf(object);
  26547 
  26548     // Zig allows dereferencing a single pointer during field lookup. Note that
  26549     // we don't actually need to generate the dereference some field lookups, like the
  26550     // length of arrays and other comptime operations.
  26551     const is_pointer_to = object_ty.isSinglePointer(zcu);
  26552 
  26553     const inner_ty = if (is_pointer_to)
  26554         object_ty.childType(zcu)
  26555     else
  26556         object_ty;
  26557 
  26558     switch (inner_ty.zigTypeTag(zcu)) {
  26559         .array => {
  26560             if (field_name.eqlSlice("len", ip)) {
  26561                 return Air.internedToRef((try pt.intValue(.usize, inner_ty.arrayLen(zcu))).toIntern());
  26562             } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
  26563                 const ptr_info = object_ty.ptrInfo(zcu);
  26564                 const result_ty = try pt.ptrTypeSema(.{
  26565                     .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(),
  26566                     .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  26567                     .flags = .{
  26568                         .size = .many,
  26569                         .alignment = ptr_info.flags.alignment,
  26570                         .is_const = ptr_info.flags.is_const,
  26571                         .is_volatile = ptr_info.flags.is_volatile,
  26572                         .is_allowzero = ptr_info.flags.is_allowzero,
  26573                         .address_space = ptr_info.flags.address_space,
  26574                         .vector_index = ptr_info.flags.vector_index,
  26575                     },
  26576                     .packed_offset = ptr_info.packed_offset,
  26577                 });
  26578                 return sema.coerce(block, result_ty, object, src);
  26579             } else {
  26580                 return sema.fail(
  26581                     block,
  26582                     field_name_src,
  26583                     "no member named '{f}' in '{f}'",
  26584                     .{ field_name.fmt(ip), object_ty.fmt(pt) },
  26585                 );
  26586             }
  26587         },
  26588         .pointer => {
  26589             const ptr_info = inner_ty.ptrInfo(zcu);
  26590             if (ptr_info.flags.size == .slice) {
  26591                 if (field_name.eqlSlice("ptr", ip)) {
  26592                     const slice = if (is_pointer_to)
  26593                         try sema.analyzeLoad(block, src, object, object_src)
  26594                     else
  26595                         object;
  26596                     return sema.analyzeSlicePtr(block, object_src, slice, inner_ty);
  26597                 } else if (field_name.eqlSlice("len", ip)) {
  26598                     const slice = if (is_pointer_to)
  26599                         try sema.analyzeLoad(block, src, object, object_src)
  26600                     else
  26601                         object;
  26602                     return sema.analyzeSliceLen(block, src, slice);
  26603                 } else {
  26604                     return sema.fail(
  26605                         block,
  26606                         field_name_src,
  26607                         "no member named '{f}' in '{f}'",
  26608                         .{ field_name.fmt(ip), object_ty.fmt(pt) },
  26609                     );
  26610                 }
  26611             }
  26612         },
  26613         .type => {
  26614             const dereffed_type = if (is_pointer_to)
  26615                 try sema.analyzeLoad(block, src, object, object_src)
  26616             else
  26617                 object;
  26618 
  26619             const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
  26620             const child_type = val.toType();
  26621 
  26622             switch (child_type.zigTypeTag(zcu)) {
  26623                 .error_set => {
  26624                     switch (ip.indexToKey(child_type.toIntern())) {
  26625                         .error_set_type => |error_set_type| blk: {
  26626                             if (error_set_type.nameIndex(ip, field_name) != null) break :blk;
  26627                             return sema.fail(block, src, "no error named '{f}' in '{f}'", .{
  26628                                 field_name.fmt(ip), child_type.fmt(pt),
  26629                             });
  26630                         },
  26631                         .inferred_error_set_type => {
  26632                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  26633                         },
  26634                         .simple_type => |t| {
  26635                             assert(t == .anyerror);
  26636                             _ = try pt.getErrorValue(field_name);
  26637                         },
  26638                         else => unreachable,
  26639                     }
  26640 
  26641                     const error_set_type = if (!child_type.isAnyError(zcu))
  26642                         child_type
  26643                     else
  26644                         try pt.singleErrorSetType(field_name);
  26645                     return Air.internedToRef((try pt.intern(.{ .err = .{
  26646                         .ty = error_set_type.toIntern(),
  26647                         .name = field_name,
  26648                     } })));
  26649                 },
  26650                 .@"union" => {
  26651                     if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  26652                         return inst;
  26653                     }
  26654                     try child_type.resolveFields(pt);
  26655                     if (child_type.unionTagType(zcu)) |enum_ty| {
  26656                         if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| {
  26657                             const field_index: u32 = @intCast(field_index_usize);
  26658                             return Air.internedToRef((try pt.enumValueFieldIndex(enum_ty, field_index)).toIntern());
  26659                         }
  26660                     }
  26661                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26662                 },
  26663                 .@"enum" => {
  26664                     if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  26665                         return inst;
  26666                     }
  26667                     const field_index_usize = child_type.enumFieldIndex(field_name, zcu) orelse
  26668                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26669                     const field_index: u32 = @intCast(field_index_usize);
  26670                     const enum_val = try pt.enumValueFieldIndex(child_type, field_index);
  26671                     return Air.internedToRef(enum_val.toIntern());
  26672                 },
  26673                 .@"struct", .@"opaque" => {
  26674                     if (!child_type.isTuple(zcu) and child_type.toIntern() != .anyopaque_type) {
  26675                         if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  26676                             return inst;
  26677                         }
  26678                     }
  26679                     return sema.failWithBadMemberAccess(block, child_type, src, field_name);
  26680                 },
  26681                 else => return sema.failWithOwnedErrorMsg(block, msg: {
  26682                     const msg = try sema.errMsg(src, "type '{f}' has no members", .{child_type.fmt(pt)});
  26683                     errdefer msg.destroy(sema.gpa);
  26684                     if (child_type.isSlice(zcu)) try sema.errNote(src, msg, "slice values have 'len' and 'ptr' members", .{});
  26685                     if (child_type.zigTypeTag(zcu) == .array) try sema.errNote(src, msg, "array values have 'len' member", .{});
  26686                     break :msg msg;
  26687                 }),
  26688             }
  26689         },
  26690         .@"struct" => if (is_pointer_to) {
  26691             // Avoid loading the entire struct by fetching a pointer and loading that
  26692             const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  26693             return sema.analyzeLoad(block, src, field_ptr, object_src);
  26694         } else {
  26695             return sema.structFieldVal(block, object, field_name, field_name_src, inner_ty);
  26696         },
  26697         .@"union" => if (is_pointer_to) {
  26698             // Avoid loading the entire union by fetching a pointer and loading that
  26699             const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
  26700             return sema.analyzeLoad(block, src, field_ptr, object_src);
  26701         } else {
  26702             return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
  26703         },
  26704         else => {},
  26705     }
  26706     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  26707 }
  26708 
  26709 fn fieldPtr(
  26710     sema: *Sema,
  26711     block: *Block,
  26712     src: LazySrcLoc,
  26713     object_ptr: Air.Inst.Ref,
  26714     field_name: InternPool.NullTerminatedString,
  26715     field_name_src: LazySrcLoc,
  26716     initializing: bool,
  26717 ) CompileError!Air.Inst.Ref {
  26718     // When editing this function, note that there is corresponding logic to be edited
  26719     // in `fieldVal`. This function takes a pointer and returns a pointer.
  26720 
  26721     const pt = sema.pt;
  26722     const zcu = pt.zcu;
  26723     const ip = &zcu.intern_pool;
  26724     const object_ptr_src = src; // TODO better source location
  26725     const object_ptr_ty = sema.typeOf(object_ptr);
  26726     const object_ty = switch (object_ptr_ty.zigTypeTag(zcu)) {
  26727         .pointer => object_ptr_ty.childType(zcu),
  26728         else => return sema.fail(block, object_ptr_src, "expected pointer, found '{f}'", .{object_ptr_ty.fmt(pt)}),
  26729     };
  26730 
  26731     // Zig allows dereferencing a single pointer during field lookup. Note that
  26732     // we don't actually need to generate the dereference some field lookups, like the
  26733     // length of arrays and other comptime operations.
  26734     const is_pointer_to = object_ty.isSinglePointer(zcu);
  26735 
  26736     const inner_ty = if (is_pointer_to)
  26737         object_ty.childType(zcu)
  26738     else
  26739         object_ty;
  26740 
  26741     switch (inner_ty.zigTypeTag(zcu)) {
  26742         .array => {
  26743             if (field_name.eqlSlice("len", ip)) {
  26744                 const int_val = try pt.intValue(.usize, inner_ty.arrayLen(zcu));
  26745                 return uavRef(sema, int_val.toIntern());
  26746             } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
  26747                 const ptr_info = object_ty.ptrInfo(zcu);
  26748                 const new_ptr_ty = try pt.ptrTypeSema(.{
  26749                     .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(),
  26750                     .sentinel = if (object_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  26751                     .flags = .{
  26752                         .size = .many,
  26753                         .alignment = ptr_info.flags.alignment,
  26754                         .is_const = ptr_info.flags.is_const,
  26755                         .is_volatile = ptr_info.flags.is_volatile,
  26756                         .is_allowzero = ptr_info.flags.is_allowzero,
  26757                         .address_space = ptr_info.flags.address_space,
  26758                         .vector_index = ptr_info.flags.vector_index,
  26759                     },
  26760                     .packed_offset = ptr_info.packed_offset,
  26761                 });
  26762                 const ptr_ptr_info = object_ptr_ty.ptrInfo(zcu);
  26763                 const result_ty = try pt.ptrTypeSema(.{
  26764                     .child = new_ptr_ty.toIntern(),
  26765                     .sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
  26766                     .flags = .{
  26767                         .alignment = ptr_ptr_info.flags.alignment,
  26768                         .is_const = ptr_ptr_info.flags.is_const,
  26769                         .is_volatile = ptr_ptr_info.flags.is_volatile,
  26770                         .is_allowzero = ptr_ptr_info.flags.is_allowzero,
  26771                         .address_space = ptr_ptr_info.flags.address_space,
  26772                         .vector_index = ptr_ptr_info.flags.vector_index,
  26773                     },
  26774                     .packed_offset = ptr_ptr_info.packed_offset,
  26775                 });
  26776                 return sema.bitCast(block, result_ty, object_ptr, src, null);
  26777             } else {
  26778                 return sema.fail(
  26779                     block,
  26780                     field_name_src,
  26781                     "no member named '{f}' in '{f}'",
  26782                     .{ field_name.fmt(ip), object_ty.fmt(pt) },
  26783                 );
  26784             }
  26785         },
  26786         .pointer => if (inner_ty.isSlice(zcu)) {
  26787             const inner_ptr = if (is_pointer_to)
  26788                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  26789             else
  26790                 object_ptr;
  26791 
  26792             const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty;
  26793 
  26794             if (field_name.eqlSlice("ptr", ip)) {
  26795                 const slice_ptr_ty = inner_ty.slicePtrFieldType(zcu);
  26796 
  26797                 const result_ty = try pt.ptrTypeSema(.{
  26798                     .child = slice_ptr_ty.toIntern(),
  26799                     .flags = .{
  26800                         .is_const = !attr_ptr_ty.ptrIsMutable(zcu),
  26801                         .is_volatile = attr_ptr_ty.isVolatilePtr(zcu),
  26802                         .address_space = attr_ptr_ty.ptrAddressSpace(zcu),
  26803                     },
  26804                 });
  26805 
  26806                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  26807                     return Air.internedToRef((try val.ptrField(Value.slice_ptr_index, pt)).toIntern());
  26808                 }
  26809                 try sema.requireRuntimeBlock(block, src, null);
  26810 
  26811                 const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
  26812                 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
  26813                 return field_ptr;
  26814             } else if (field_name.eqlSlice("len", ip)) {
  26815                 const result_ty = try pt.ptrTypeSema(.{
  26816                     .child = .usize_type,
  26817                     .flags = .{
  26818                         .is_const = !attr_ptr_ty.ptrIsMutable(zcu),
  26819                         .is_volatile = attr_ptr_ty.isVolatilePtr(zcu),
  26820                         .address_space = attr_ptr_ty.ptrAddressSpace(zcu),
  26821                     },
  26822                 });
  26823 
  26824                 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
  26825                     return Air.internedToRef((try val.ptrField(Value.slice_len_index, pt)).toIntern());
  26826                 }
  26827                 try sema.requireRuntimeBlock(block, src, null);
  26828 
  26829                 const field_ptr = try block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr);
  26830                 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
  26831                 return field_ptr;
  26832             } else {
  26833                 return sema.fail(
  26834                     block,
  26835                     field_name_src,
  26836                     "no member named '{f}' in '{f}'",
  26837                     .{ field_name.fmt(ip), object_ty.fmt(pt) },
  26838                 );
  26839             }
  26840         },
  26841         .type => {
  26842             _ = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, object_ptr, undefined);
  26843             const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
  26844             const inner = if (is_pointer_to)
  26845                 try sema.analyzeLoad(block, src, result, object_ptr_src)
  26846             else
  26847                 result;
  26848 
  26849             const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
  26850             const child_type = val.toType();
  26851 
  26852             switch (child_type.zigTypeTag(zcu)) {
  26853                 .error_set => {
  26854                     switch (ip.indexToKey(child_type.toIntern())) {
  26855                         .error_set_type => |error_set_type| blk: {
  26856                             if (error_set_type.nameIndex(ip, field_name) != null) {
  26857                                 break :blk;
  26858                             }
  26859                             return sema.fail(block, src, "no error named '{f}' in '{f}'", .{
  26860                                 field_name.fmt(ip), child_type.fmt(pt),
  26861                             });
  26862                         },
  26863                         .inferred_error_set_type => {
  26864                             return sema.fail(block, src, "TODO handle inferred error sets here", .{});
  26865                         },
  26866                         .simple_type => |t| {
  26867                             assert(t == .anyerror);
  26868                             _ = try pt.getErrorValue(field_name);
  26869                         },
  26870                         else => unreachable,
  26871                     }
  26872 
  26873                     const error_set_type = if (!child_type.isAnyError(zcu))
  26874                         child_type
  26875                     else
  26876                         try pt.singleErrorSetType(field_name);
  26877                     return uavRef(sema, try pt.intern(.{ .err = .{
  26878                         .ty = error_set_type.toIntern(),
  26879                         .name = field_name,
  26880                     } }));
  26881                 },
  26882                 .@"union" => {
  26883                     if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  26884                         return inst;
  26885                     }
  26886                     try child_type.resolveFields(pt);
  26887                     if (child_type.unionTagType(zcu)) |enum_ty| {
  26888                         if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| {
  26889                             const field_index_u32: u32 = @intCast(field_index);
  26890                             const idx_val = try pt.enumValueFieldIndex(enum_ty, field_index_u32);
  26891                             return uavRef(sema, idx_val.toIntern());
  26892                         }
  26893                     }
  26894                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26895                 },
  26896                 .@"enum" => {
  26897                     if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  26898                         return inst;
  26899                     }
  26900                     const field_index = child_type.enumFieldIndex(field_name, zcu) orelse {
  26901                         return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26902                     };
  26903                     const field_index_u32: u32 = @intCast(field_index);
  26904                     const idx_val = try pt.enumValueFieldIndex(child_type, field_index_u32);
  26905                     return uavRef(sema, idx_val.toIntern());
  26906                 },
  26907                 .@"struct", .@"opaque" => {
  26908                     if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
  26909                         return inst;
  26910                     }
  26911                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
  26912                 },
  26913                 else => return sema.fail(block, src, "type '{f}' has no members", .{child_type.fmt(pt)}),
  26914             }
  26915         },
  26916         .@"struct" => {
  26917             const inner_ptr = if (is_pointer_to)
  26918                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  26919             else
  26920                 object_ptr;
  26921             const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  26922             try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
  26923             return field_ptr;
  26924         },
  26925         .@"union" => {
  26926             const inner_ptr = if (is_pointer_to)
  26927                 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
  26928             else
  26929                 object_ptr;
  26930             const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
  26931             try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
  26932             return field_ptr;
  26933         },
  26934         else => {},
  26935     }
  26936     return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
  26937 }
  26938 
  26939 const ResolvedFieldCallee = union(enum) {
  26940     /// The LHS of the call was an actual field with this value.
  26941     direct: Air.Inst.Ref,
  26942     /// This is a method call, with the function and first argument given.
  26943     method: struct {
  26944         func_inst: Air.Inst.Ref,
  26945         arg0_inst: Air.Inst.Ref,
  26946     },
  26947 };
  26948 
  26949 fn fieldCallBind(
  26950     sema: *Sema,
  26951     block: *Block,
  26952     src: LazySrcLoc,
  26953     raw_ptr: Air.Inst.Ref,
  26954     field_name: InternPool.NullTerminatedString,
  26955     field_name_src: LazySrcLoc,
  26956 ) CompileError!ResolvedFieldCallee {
  26957     // When editing this function, note that there is corresponding logic to be edited
  26958     // in `fieldVal`. This function takes a pointer and returns a pointer.
  26959 
  26960     const pt = sema.pt;
  26961     const zcu = pt.zcu;
  26962     const ip = &zcu.intern_pool;
  26963     const raw_ptr_src = src; // TODO better source location
  26964     const raw_ptr_ty = sema.typeOf(raw_ptr);
  26965     const inner_ty = if (raw_ptr_ty.zigTypeTag(zcu) == .pointer and (raw_ptr_ty.ptrSize(zcu) == .one or raw_ptr_ty.ptrSize(zcu) == .c))
  26966         raw_ptr_ty.childType(zcu)
  26967     else
  26968         return sema.fail(block, raw_ptr_src, "expected single pointer, found '{f}'", .{raw_ptr_ty.fmt(pt)});
  26969 
  26970     // Optionally dereference a second pointer to get the concrete type.
  26971     const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one;
  26972     const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty;
  26973     const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty;
  26974     const object_ptr = if (is_double_ptr)
  26975         try sema.analyzeLoad(block, src, raw_ptr, src)
  26976     else
  26977         raw_ptr;
  26978 
  26979     find_field: {
  26980         switch (concrete_ty.zigTypeTag(zcu)) {
  26981             .@"struct" => {
  26982                 try concrete_ty.resolveFields(pt);
  26983                 if (zcu.typeToStruct(concrete_ty)) |struct_type| {
  26984                     const field_index = struct_type.nameIndex(ip, field_name) orelse
  26985                         break :find_field;
  26986                     const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
  26987 
  26988                     return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr);
  26989                 } else if (concrete_ty.isTuple(zcu)) {
  26990                     if (field_name.eqlSlice("len", ip)) {
  26991                         return .{ .direct = try pt.intRef(.usize, concrete_ty.structFieldCount(zcu)) };
  26992                     }
  26993                     if (field_name.toUnsigned(ip)) |field_index| {
  26994                         if (field_index >= concrete_ty.structFieldCount(zcu)) break :find_field;
  26995                         return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.fieldType(field_index, zcu), field_index, object_ptr);
  26996                     }
  26997                 } else {
  26998                     const max = concrete_ty.structFieldCount(zcu);
  26999                     for (0..max) |i_usize| {
  27000                         const i: u32 = @intCast(i_usize);
  27001                         if (field_name == concrete_ty.structFieldName(i, zcu).unwrap().?) {
  27002                             return sema.finishFieldCallBind(block, src, ptr_ty, concrete_ty.fieldType(i, zcu), i, object_ptr);
  27003                         }
  27004                     }
  27005                 }
  27006             },
  27007             .@"union" => {
  27008                 try concrete_ty.resolveFields(pt);
  27009                 const union_obj = zcu.typeToUnion(concrete_ty).?;
  27010                 _ = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse break :find_field;
  27011                 const field_ptr = try unionFieldPtr(sema, block, src, object_ptr, field_name, field_name_src, concrete_ty, false);
  27012                 return .{ .direct = try sema.analyzeLoad(block, src, field_ptr, src) };
  27013             },
  27014             .type => {
  27015                 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
  27016                 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) };
  27017             },
  27018             else => {},
  27019         }
  27020     }
  27021 
  27022     // If we get here, we need to look for a decl in the struct type instead.
  27023     const found_nav = found_nav: {
  27024         const namespace = concrete_ty.getNamespace(zcu).unwrap() orelse
  27025             break :found_nav null;
  27026         const nav_index = try sema.namespaceLookup(block, src, namespace, field_name) orelse
  27027             break :found_nav null;
  27028 
  27029         const decl_val = try sema.analyzeNavVal(block, src, nav_index);
  27030         const decl_type = sema.typeOf(decl_val);
  27031         if (zcu.typeToFunc(decl_type)) |func_type| f: {
  27032             if (func_type.param_types.len == 0) break :f;
  27033 
  27034             const first_param_type: Type = .fromInterned(func_type.param_types.get(ip)[0]);
  27035             if (first_param_type.isGenericPoison() or
  27036                 (first_param_type.zigTypeTag(zcu) == .pointer and
  27037                     (first_param_type.ptrSize(zcu) == .one or
  27038                         first_param_type.ptrSize(zcu) == .c) and
  27039                     first_param_type.childType(zcu).eql(concrete_ty, zcu)))
  27040             {
  27041                 // Note that if the param type is generic poison, we know that it must
  27042                 // specifically be `anytype` since it's the first parameter, meaning we
  27043                 // can safely assume it can be a pointer.
  27044                 // TODO: bound fn calls on rvalues should probably
  27045                 // generate a by-value argument somehow.
  27046                 return .{ .method = .{
  27047                     .func_inst = decl_val,
  27048                     .arg0_inst = object_ptr,
  27049                 } };
  27050             } else if (first_param_type.eql(concrete_ty, zcu)) {
  27051                 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  27052                 return .{ .method = .{
  27053                     .func_inst = decl_val,
  27054                     .arg0_inst = deref,
  27055                 } };
  27056             } else if (first_param_type.zigTypeTag(zcu) == .optional) {
  27057                 const child = first_param_type.optionalChild(zcu);
  27058                 if (child.eql(concrete_ty, zcu)) {
  27059                     const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  27060                     return .{ .method = .{
  27061                         .func_inst = decl_val,
  27062                         .arg0_inst = deref,
  27063                     } };
  27064                 } else if (child.zigTypeTag(zcu) == .pointer and
  27065                     child.ptrSize(zcu) == .one and
  27066                     child.childType(zcu).eql(concrete_ty, zcu))
  27067                 {
  27068                     return .{ .method = .{
  27069                         .func_inst = decl_val,
  27070                         .arg0_inst = object_ptr,
  27071                     } };
  27072                 }
  27073             } else if (first_param_type.zigTypeTag(zcu) == .error_union and
  27074                 first_param_type.errorUnionPayload(zcu).eql(concrete_ty, zcu))
  27075             {
  27076                 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
  27077                 return .{ .method = .{
  27078                     .func_inst = decl_val,
  27079                     .arg0_inst = deref,
  27080                 } };
  27081             }
  27082         }
  27083         break :found_nav nav_index;
  27084     };
  27085 
  27086     const msg = msg: {
  27087         const msg = try sema.errMsg(src, "no field or member function named '{f}' in '{f}'", .{
  27088             field_name.fmt(ip),
  27089             concrete_ty.fmt(pt),
  27090         });
  27091         errdefer msg.destroy(sema.gpa);
  27092         try sema.addDeclaredHereNote(msg, concrete_ty);
  27093         if (found_nav) |nav_index| {
  27094             try sema.errNote(
  27095                 zcu.navSrcLoc(nav_index),
  27096                 msg,
  27097                 "'{f}' is not a member function",
  27098                 .{field_name.fmt(ip)},
  27099             );
  27100         }
  27101         if (concrete_ty.zigTypeTag(zcu) == .error_union) {
  27102             try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
  27103         }
  27104         if (is_double_ptr) {
  27105             try sema.errNote(src, msg, "method invocation only supports up to one level of implicit pointer dereferencing", .{});
  27106             try sema.errNote(src, msg, "use '.*' to dereference pointer", .{});
  27107         }
  27108         break :msg msg;
  27109     };
  27110     return sema.failWithOwnedErrorMsg(block, msg);
  27111 }
  27112 
  27113 fn finishFieldCallBind(
  27114     sema: *Sema,
  27115     block: *Block,
  27116     src: LazySrcLoc,
  27117     ptr_ty: Type,
  27118     field_ty: Type,
  27119     field_index: u32,
  27120     object_ptr: Air.Inst.Ref,
  27121 ) CompileError!ResolvedFieldCallee {
  27122     const pt = sema.pt;
  27123     const zcu = pt.zcu;
  27124     const ptr_field_ty = try pt.ptrTypeSema(.{
  27125         .child = field_ty.toIntern(),
  27126         .flags = .{
  27127             .is_const = !ptr_ty.ptrIsMutable(zcu),
  27128             .address_space = ptr_ty.ptrAddressSpace(zcu),
  27129         },
  27130     });
  27131 
  27132     const container_ty = ptr_ty.childType(zcu);
  27133     if (container_ty.zigTypeTag(zcu) == .@"struct") {
  27134         if (container_ty.structFieldIsComptime(field_index, zcu)) {
  27135             try container_ty.resolveStructFieldInits(pt);
  27136             const default_val = (try container_ty.structFieldValueComptime(pt, field_index)).?;
  27137             return .{ .direct = Air.internedToRef(default_val.toIntern()) };
  27138         }
  27139     }
  27140 
  27141     if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
  27142         const ptr_val = try struct_ptr_val.ptrField(field_index, pt);
  27143         const pointer = Air.internedToRef(ptr_val.toIntern());
  27144         return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) };
  27145     }
  27146 
  27147     try sema.requireRuntimeBlock(block, src, null);
  27148     const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
  27149     return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) };
  27150 }
  27151 
  27152 fn namespaceLookup(
  27153     sema: *Sema,
  27154     block: *Block,
  27155     src: LazySrcLoc,
  27156     namespace: InternPool.NamespaceIndex,
  27157     decl_name: InternPool.NullTerminatedString,
  27158 ) CompileError!?InternPool.Nav.Index {
  27159     const pt = sema.pt;
  27160     const zcu = pt.zcu;
  27161     const gpa = sema.gpa;
  27162     if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| {
  27163         if (!lookup.accessible) {
  27164             return sema.failWithOwnedErrorMsg(block, msg: {
  27165                 const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{
  27166                     decl_name.fmt(&zcu.intern_pool),
  27167                 });
  27168                 errdefer msg.destroy(gpa);
  27169                 try sema.errNote(zcu.navSrcLoc(lookup.nav), msg, "declared here", .{});
  27170                 break :msg msg;
  27171             });
  27172         }
  27173         return lookup.nav;
  27174     }
  27175     return null;
  27176 }
  27177 
  27178 fn namespaceLookupRef(
  27179     sema: *Sema,
  27180     block: *Block,
  27181     src: LazySrcLoc,
  27182     namespace: InternPool.NamespaceIndex,
  27183     decl_name: InternPool.NullTerminatedString,
  27184 ) CompileError!?Air.Inst.Ref {
  27185     const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null;
  27186     return try sema.analyzeNavRef(block, src, nav);
  27187 }
  27188 
  27189 fn namespaceLookupVal(
  27190     sema: *Sema,
  27191     block: *Block,
  27192     src: LazySrcLoc,
  27193     namespace: InternPool.NamespaceIndex,
  27194     decl_name: InternPool.NullTerminatedString,
  27195 ) CompileError!?Air.Inst.Ref {
  27196     const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null;
  27197     return try sema.analyzeNavVal(block, src, nav);
  27198 }
  27199 
  27200 fn structFieldPtr(
  27201     sema: *Sema,
  27202     block: *Block,
  27203     src: LazySrcLoc,
  27204     struct_ptr: Air.Inst.Ref,
  27205     field_name: InternPool.NullTerminatedString,
  27206     field_name_src: LazySrcLoc,
  27207     struct_ty: Type,
  27208     initializing: bool,
  27209 ) CompileError!Air.Inst.Ref {
  27210     const pt = sema.pt;
  27211     const zcu = pt.zcu;
  27212     const ip = &zcu.intern_pool;
  27213     assert(struct_ty.zigTypeTag(zcu) == .@"struct");
  27214 
  27215     try struct_ty.resolveFields(pt);
  27216     try struct_ty.resolveLayout(pt);
  27217 
  27218     if (struct_ty.isTuple(zcu)) {
  27219         if (field_name.eqlSlice("len", ip)) {
  27220             const len_inst = try pt.intRef(.usize, struct_ty.structFieldCount(zcu));
  27221             return sema.analyzeRef(block, src, len_inst);
  27222         }
  27223         const field_index = try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
  27224         return sema.tupleFieldPtr(block, src, struct_ptr, field_name_src, field_index, initializing);
  27225     }
  27226 
  27227     const struct_type = zcu.typeToStruct(struct_ty).?;
  27228 
  27229     const field_index = struct_type.nameIndex(ip, field_name) orelse
  27230         return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
  27231 
  27232     return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty);
  27233 }
  27234 
  27235 fn structFieldPtrByIndex(
  27236     sema: *Sema,
  27237     block: *Block,
  27238     src: LazySrcLoc,
  27239     struct_ptr: Air.Inst.Ref,
  27240     field_index: u32,
  27241     struct_ty: Type,
  27242 ) CompileError!Air.Inst.Ref {
  27243     const pt = sema.pt;
  27244     const zcu = pt.zcu;
  27245     const ip = &zcu.intern_pool;
  27246 
  27247     const struct_type = zcu.typeToStruct(struct_ty).?;
  27248     const field_is_comptime = struct_type.fieldIsComptime(ip, field_index);
  27249 
  27250     // Comptime fields are handled later
  27251     if (!field_is_comptime) {
  27252         if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
  27253             const val = try struct_ptr_val.ptrField(field_index, pt);
  27254             return Air.internedToRef(val.toIntern());
  27255         }
  27256     }
  27257 
  27258     const field_ty = struct_type.field_types.get(ip)[field_index];
  27259     const struct_ptr_ty = sema.typeOf(struct_ptr);
  27260     const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu);
  27261 
  27262     var ptr_ty_data: InternPool.Key.PtrType = .{
  27263         .child = field_ty,
  27264         .flags = .{
  27265             .is_const = struct_ptr_ty_info.flags.is_const,
  27266             .is_volatile = struct_ptr_ty_info.flags.is_volatile,
  27267             .address_space = struct_ptr_ty_info.flags.address_space,
  27268         },
  27269     };
  27270 
  27271     const parent_align = if (struct_ptr_ty_info.flags.alignment != .none)
  27272         struct_ptr_ty_info.flags.alignment
  27273     else
  27274         try Type.fromInterned(struct_ptr_ty_info.child).abiAlignmentSema(pt);
  27275 
  27276     if (struct_type.layout == .@"packed") {
  27277         assert(!field_is_comptime);
  27278         switch (struct_ty.packedStructFieldPtrInfo(struct_ptr_ty, field_index, pt)) {
  27279             .bit_ptr => |packed_offset| {
  27280                 ptr_ty_data.flags.alignment = parent_align;
  27281                 ptr_ty_data.packed_offset = packed_offset;
  27282             },
  27283             .byte_ptr => |ptr_info| {
  27284                 ptr_ty_data.flags.alignment = ptr_info.alignment;
  27285             },
  27286         }
  27287     } else if (struct_type.layout == .@"extern") {
  27288         assert(!field_is_comptime);
  27289         // For extern structs, field alignment might be bigger than type's
  27290         // natural alignment. Eg, in `extern struct { x: u32, y: u16 }` the
  27291         // second field is aligned as u32.
  27292         const field_offset = struct_ty.structFieldOffset(field_index, zcu);
  27293         ptr_ty_data.flags.alignment = if (parent_align == .none)
  27294             .none
  27295         else
  27296             @enumFromInt(@min(@intFromEnum(parent_align), @ctz(field_offset)));
  27297     } else {
  27298         // Our alignment is capped at the field alignment.
  27299         const field_align = try Type.fromInterned(field_ty).structFieldAlignmentSema(
  27300             struct_type.fieldAlign(ip, field_index),
  27301             struct_type.layout,
  27302             pt,
  27303         );
  27304         ptr_ty_data.flags.alignment = if (struct_ptr_ty_info.flags.alignment == .none)
  27305             field_align
  27306         else
  27307             field_align.min(parent_align);
  27308     }
  27309 
  27310     const ptr_field_ty = try pt.ptrTypeSema(ptr_ty_data);
  27311 
  27312     if (field_is_comptime) {
  27313         try struct_ty.resolveStructFieldInits(pt);
  27314         const val = try pt.intern(.{ .ptr = .{
  27315             .ty = ptr_field_ty.toIntern(),
  27316             .base_addr = .{ .comptime_field = struct_type.field_inits.get(ip)[field_index] },
  27317             .byte_offset = 0,
  27318         } });
  27319         return Air.internedToRef(val);
  27320     }
  27321 
  27322     return block.addStructFieldPtr(struct_ptr, field_index, ptr_field_ty);
  27323 }
  27324 
  27325 fn structFieldVal(
  27326     sema: *Sema,
  27327     block: *Block,
  27328     struct_byval: Air.Inst.Ref,
  27329     field_name: InternPool.NullTerminatedString,
  27330     field_name_src: LazySrcLoc,
  27331     struct_ty: Type,
  27332 ) CompileError!Air.Inst.Ref {
  27333     const pt = sema.pt;
  27334     const zcu = pt.zcu;
  27335     const ip = &zcu.intern_pool;
  27336     assert(struct_ty.zigTypeTag(zcu) == .@"struct");
  27337 
  27338     try struct_ty.resolveFields(pt);
  27339 
  27340     switch (ip.indexToKey(struct_ty.toIntern())) {
  27341         .struct_type => {
  27342             const struct_type = ip.loadStructType(struct_ty.toIntern());
  27343 
  27344             const field_index = struct_type.nameIndex(ip, field_name) orelse
  27345                 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
  27346             if (struct_type.fieldIsComptime(ip, field_index)) {
  27347                 try struct_ty.resolveStructFieldInits(pt);
  27348                 return Air.internedToRef(struct_type.field_inits.get(ip)[field_index]);
  27349             }
  27350 
  27351             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
  27352             if (try sema.typeHasOnePossibleValue(field_ty)) |field_val|
  27353                 return Air.internedToRef(field_val.toIntern());
  27354 
  27355             if (try sema.resolveValue(struct_byval)) |struct_val| {
  27356                 if (struct_val.isUndef(zcu)) return pt.undefRef(field_ty);
  27357                 if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
  27358                     return Air.internedToRef(opv.toIntern());
  27359                 }
  27360                 return Air.internedToRef((try struct_val.fieldValue(pt, field_index)).toIntern());
  27361             }
  27362 
  27363             try field_ty.resolveLayout(pt);
  27364             return block.addStructFieldVal(struct_byval, field_index, field_ty);
  27365         },
  27366         .tuple_type => {
  27367             return sema.tupleFieldVal(block, struct_byval, field_name, field_name_src, struct_ty);
  27368         },
  27369         else => unreachable,
  27370     }
  27371 }
  27372 
  27373 fn tupleFieldVal(
  27374     sema: *Sema,
  27375     block: *Block,
  27376     tuple_byval: Air.Inst.Ref,
  27377     field_name: InternPool.NullTerminatedString,
  27378     field_name_src: LazySrcLoc,
  27379     tuple_ty: Type,
  27380 ) CompileError!Air.Inst.Ref {
  27381     const pt = sema.pt;
  27382     const zcu = pt.zcu;
  27383     if (field_name.eqlSlice("len", &zcu.intern_pool)) {
  27384         return pt.intRef(.usize, tuple_ty.structFieldCount(zcu));
  27385     }
  27386     const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src);
  27387     return sema.tupleFieldValByIndex(block, tuple_byval, field_index, tuple_ty);
  27388 }
  27389 
  27390 /// Asserts that `field_name` is not "len".
  27391 fn tupleFieldIndex(
  27392     sema: *Sema,
  27393     block: *Block,
  27394     tuple_ty: Type,
  27395     field_name: InternPool.NullTerminatedString,
  27396     field_name_src: LazySrcLoc,
  27397 ) CompileError!u32 {
  27398     const pt = sema.pt;
  27399     const ip = &pt.zcu.intern_pool;
  27400     assert(!field_name.eqlSlice("len", ip));
  27401     if (field_name.toUnsigned(ip)) |field_index| {
  27402         if (field_index < tuple_ty.structFieldCount(pt.zcu)) return field_index;
  27403         return sema.fail(block, field_name_src, "index '{f}' out of bounds of tuple '{f}'", .{
  27404             field_name.fmt(ip), tuple_ty.fmt(pt),
  27405         });
  27406     }
  27407 
  27408     return sema.fail(block, field_name_src, "no field named '{f}' in tuple '{f}'", .{
  27409         field_name.fmt(ip), tuple_ty.fmt(pt),
  27410     });
  27411 }
  27412 
  27413 fn tupleFieldValByIndex(
  27414     sema: *Sema,
  27415     block: *Block,
  27416     tuple_byval: Air.Inst.Ref,
  27417     field_index: u32,
  27418     tuple_ty: Type,
  27419 ) CompileError!Air.Inst.Ref {
  27420     const pt = sema.pt;
  27421     const zcu = pt.zcu;
  27422     const field_ty = tuple_ty.fieldType(field_index, zcu);
  27423 
  27424     if (tuple_ty.structFieldIsComptime(field_index, zcu))
  27425         try tuple_ty.resolveStructFieldInits(pt);
  27426     if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| {
  27427         return Air.internedToRef(default_value.toIntern());
  27428     }
  27429 
  27430     if (try sema.resolveValue(tuple_byval)) |tuple_val| {
  27431         if ((try sema.typeHasOnePossibleValue(field_ty))) |opv| {
  27432             return Air.internedToRef(opv.toIntern());
  27433         }
  27434         return switch (zcu.intern_pool.indexToKey(tuple_val.toIntern())) {
  27435             .undef => pt.undefRef(field_ty),
  27436             .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) {
  27437                 .bytes => |bytes| try pt.intValue(.u8, bytes.at(field_index, &zcu.intern_pool)),
  27438                 .elems => |elems| Value.fromInterned(elems[field_index]),
  27439                 .repeated_elem => |elem| Value.fromInterned(elem),
  27440             }.toIntern()),
  27441             else => unreachable,
  27442         };
  27443     }
  27444 
  27445     try field_ty.resolveLayout(pt);
  27446     return block.addStructFieldVal(tuple_byval, field_index, field_ty);
  27447 }
  27448 
  27449 fn unionFieldPtr(
  27450     sema: *Sema,
  27451     block: *Block,
  27452     src: LazySrcLoc,
  27453     union_ptr: Air.Inst.Ref,
  27454     field_name: InternPool.NullTerminatedString,
  27455     field_name_src: LazySrcLoc,
  27456     union_ty: Type,
  27457     initializing: bool,
  27458 ) CompileError!Air.Inst.Ref {
  27459     const pt = sema.pt;
  27460     const zcu = pt.zcu;
  27461     const ip = &zcu.intern_pool;
  27462 
  27463     assert(union_ty.zigTypeTag(zcu) == .@"union");
  27464 
  27465     const union_ptr_ty = sema.typeOf(union_ptr);
  27466     const union_ptr_info = union_ptr_ty.ptrInfo(zcu);
  27467     try union_ty.resolveFields(pt);
  27468     const union_obj = zcu.typeToUnion(union_ty).?;
  27469     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  27470     const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  27471     const ptr_field_ty = try pt.ptrTypeSema(.{
  27472         .child = field_ty.toIntern(),
  27473         .flags = .{
  27474             .is_const = union_ptr_info.flags.is_const,
  27475             .is_volatile = union_ptr_info.flags.is_volatile,
  27476             .address_space = union_ptr_info.flags.address_space,
  27477             .alignment = if (union_obj.flagsUnordered(ip).layout == .auto) blk: {
  27478                 const union_align = if (union_ptr_info.flags.alignment != .none)
  27479                     union_ptr_info.flags.alignment
  27480                 else
  27481                     try union_ty.abiAlignmentSema(pt);
  27482                 const field_align = try union_ty.fieldAlignmentSema(field_index, pt);
  27483                 break :blk union_align.min(field_align);
  27484             } else union_ptr_info.flags.alignment,
  27485         },
  27486         .packed_offset = union_ptr_info.packed_offset,
  27487     });
  27488     const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?);
  27489 
  27490     if (initializing and field_ty.zigTypeTag(zcu) == .noreturn) {
  27491         const msg = msg: {
  27492             const msg = try sema.errMsg(src, "cannot initialize 'noreturn' field of union", .{});
  27493             errdefer msg.destroy(sema.gpa);
  27494 
  27495             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
  27496                 field_name.fmt(ip),
  27497             });
  27498             try sema.addDeclaredHereNote(msg, union_ty);
  27499             break :msg msg;
  27500         };
  27501         return sema.failWithOwnedErrorMsg(block, msg);
  27502     }
  27503 
  27504     if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
  27505         switch (union_obj.flagsUnordered(ip).layout) {
  27506             .auto => if (initializing) {
  27507                 if (!sema.isComptimeMutablePtr(union_ptr_val)) {
  27508                     // The initialization is a runtime operation.
  27509                     break :ct;
  27510                 }
  27511                 // Store to the union to initialize the tag.
  27512                 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27513                 const payload_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  27514                 const new_union_val = try pt.unionValue(union_ty, field_tag, try pt.undefValue(payload_ty));
  27515                 try sema.storePtrVal(block, src, union_ptr_val, new_union_val, union_ty);
  27516             } else {
  27517                 const union_val = (try sema.pointerDeref(block, src, union_ptr_val, union_ptr_ty)) orelse
  27518                     break :ct;
  27519                 if (union_val.isUndef(zcu)) {
  27520                     return sema.failWithUseOfUndef(block, src);
  27521                 }
  27522                 const un = ip.indexToKey(union_val.toIntern()).un;
  27523                 const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27524                 const tag_matches = un.tag == field_tag.toIntern();
  27525                 if (!tag_matches) {
  27526                     const msg = msg: {
  27527                         const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
  27528                         const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu);
  27529                         const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
  27530                             field_name.fmt(ip),
  27531                             active_field_name.fmt(ip),
  27532                         });
  27533                         errdefer msg.destroy(sema.gpa);
  27534                         try sema.addDeclaredHereNote(msg, union_ty);
  27535                         break :msg msg;
  27536                     };
  27537                     return sema.failWithOwnedErrorMsg(block, msg);
  27538                 }
  27539             },
  27540             .@"packed", .@"extern" => {},
  27541         }
  27542         const field_ptr_val = try union_ptr_val.ptrField(field_index, pt);
  27543         return Air.internedToRef(field_ptr_val.toIntern());
  27544     }
  27545 
  27546     // If the union has a tag, we must either set or or safety check it depending on `initializing`.
  27547     tag: {
  27548         if (union_ty.containerLayout(zcu) != .auto) break :tag;
  27549         const tag_ty: Type = .fromInterned(union_obj.enum_tag_ty);
  27550         if (try sema.typeHasOnePossibleValue(tag_ty) != null) break :tag;
  27551         // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but
  27552         // only emit a safety check if it's available at runtime (i.e. it's safety-tagged).
  27553         const want_tag = try pt.enumValueFieldIndex(tag_ty, enum_field_index);
  27554         if (initializing) {
  27555             const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag));
  27556             try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store
  27557         } else if (block.wantSafety() and union_obj.hasTag(ip)) {
  27558             // The tag exists at runtime (safety tag), so emit a safety check.
  27559             // TODO would it be better if get_union_tag supported pointers to unions?
  27560             const union_val = try block.addTyOp(.load, union_ty, union_ptr);
  27561             const active_tag = try block.addTyOp(.get_union_tag, tag_ty, union_val);
  27562             try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(want_tag));
  27563         }
  27564     }
  27565     if (field_ty.zigTypeTag(zcu) == .noreturn) {
  27566         _ = try block.addNoOp(.unreach);
  27567         return .unreachable_value;
  27568     }
  27569     return block.addStructFieldPtr(union_ptr, field_index, ptr_field_ty);
  27570 }
  27571 
  27572 fn unionFieldVal(
  27573     sema: *Sema,
  27574     block: *Block,
  27575     src: LazySrcLoc,
  27576     union_byval: Air.Inst.Ref,
  27577     field_name: InternPool.NullTerminatedString,
  27578     field_name_src: LazySrcLoc,
  27579     union_ty: Type,
  27580 ) CompileError!Air.Inst.Ref {
  27581     const pt = sema.pt;
  27582     const zcu = pt.zcu;
  27583     const ip = &zcu.intern_pool;
  27584     assert(union_ty.zigTypeTag(zcu) == .@"union");
  27585 
  27586     try union_ty.resolveFields(pt);
  27587     const union_obj = zcu.typeToUnion(union_ty).?;
  27588     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
  27589     const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  27590     const enum_field_index: u32 = @intCast(Type.fromInterned(union_obj.enum_tag_ty).enumFieldIndex(field_name, zcu).?);
  27591 
  27592     if (try sema.resolveValue(union_byval)) |union_val| {
  27593         if (union_val.isUndef(zcu)) return pt.undefRef(field_ty);
  27594 
  27595         const un = ip.indexToKey(union_val.toIntern()).un;
  27596         const field_tag = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27597         const tag_matches = un.tag == field_tag.toIntern();
  27598         switch (union_obj.flagsUnordered(ip).layout) {
  27599             .auto => {
  27600                 if (tag_matches) {
  27601                     return Air.internedToRef(un.val);
  27602                 } else {
  27603                     const msg = msg: {
  27604                         const active_index = Type.fromInterned(union_obj.enum_tag_ty).enumTagFieldIndex(Value.fromInterned(un.tag), zcu).?;
  27605                         const active_field_name = Type.fromInterned(union_obj.enum_tag_ty).enumFieldName(active_index, zcu);
  27606                         const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
  27607                             field_name.fmt(ip), active_field_name.fmt(ip),
  27608                         });
  27609                         errdefer msg.destroy(sema.gpa);
  27610                         try sema.addDeclaredHereNote(msg, union_ty);
  27611                         break :msg msg;
  27612                     };
  27613                     return sema.failWithOwnedErrorMsg(block, msg);
  27614                 }
  27615             },
  27616             .@"extern" => if (tag_matches) {
  27617                 // Fast path - no need to use bitcast logic.
  27618                 return Air.internedToRef(un.val);
  27619             } else if (try sema.bitCastVal(union_val, field_ty, 0, 0, 0)) |field_val| {
  27620                 return Air.internedToRef(field_val.toIntern());
  27621             },
  27622             .@"packed" => if (tag_matches) {
  27623                 // Fast path - no need to use bitcast logic.
  27624                 return Air.internedToRef(un.val);
  27625             } else if (try sema.bitCastVal(union_val, field_ty, 0, try union_ty.bitSizeSema(pt), 0)) |field_val| {
  27626                 return Air.internedToRef(field_val.toIntern());
  27627             },
  27628         }
  27629     }
  27630 
  27631     if (union_obj.flagsUnordered(ip).layout == .auto and block.wantSafety() and
  27632         union_ty.unionTagTypeSafety(zcu) != null and union_obj.field_types.len > 1)
  27633     {
  27634         const wanted_tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_ty), enum_field_index);
  27635         const wanted_tag = Air.internedToRef(wanted_tag_val.toIntern());
  27636         const active_tag = try block.addTyOp(.get_union_tag, .fromInterned(union_obj.enum_tag_ty), union_byval);
  27637         try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, wanted_tag);
  27638     }
  27639 
  27640     if (field_ty.zigTypeTag(zcu) == .noreturn) {
  27641         _ = try block.addNoOp(.unreach);
  27642         return .unreachable_value;
  27643     }
  27644 
  27645     if (try sema.typeHasOnePossibleValue(field_ty)) |field_only_value| {
  27646         return Air.internedToRef(field_only_value.toIntern());
  27647     }
  27648 
  27649     try field_ty.resolveLayout(pt);
  27650     return block.addStructFieldVal(union_byval, field_index, field_ty);
  27651 }
  27652 
  27653 fn elemPtr(
  27654     sema: *Sema,
  27655     block: *Block,
  27656     src: LazySrcLoc,
  27657     indexable_ptr: Air.Inst.Ref,
  27658     elem_index: Air.Inst.Ref,
  27659     elem_index_src: LazySrcLoc,
  27660     init: bool,
  27661     oob_safety: bool,
  27662 ) CompileError!Air.Inst.Ref {
  27663     const pt = sema.pt;
  27664     const zcu = pt.zcu;
  27665     const indexable_ptr_src = src; // TODO better source location
  27666     const indexable_ptr_ty = sema.typeOf(indexable_ptr);
  27667 
  27668     const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(zcu)) {
  27669         .pointer => indexable_ptr_ty.childType(zcu),
  27670         else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}),
  27671     };
  27672     try sema.checkIndexable(block, src, indexable_ty);
  27673 
  27674     const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) {
  27675         .array, .vector => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
  27676         .@"struct" => blk: {
  27677             // Tuple field access.
  27678             const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
  27679             const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt));
  27680             break :blk try sema.tupleFieldPtr(block, src, indexable_ptr, elem_index_src, index, init);
  27681         },
  27682         else => {
  27683             const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
  27684             return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety);
  27685         },
  27686     };
  27687 
  27688     try sema.checkKnownAllocPtr(block, indexable_ptr, elem_ptr);
  27689     return elem_ptr;
  27690 }
  27691 
  27692 /// Asserts that the type of indexable is pointer.
  27693 fn elemPtrOneLayerOnly(
  27694     sema: *Sema,
  27695     block: *Block,
  27696     src: LazySrcLoc,
  27697     indexable: Air.Inst.Ref,
  27698     elem_index: Air.Inst.Ref,
  27699     elem_index_src: LazySrcLoc,
  27700     init: bool,
  27701     oob_safety: bool,
  27702 ) CompileError!Air.Inst.Ref {
  27703     const indexable_src = src; // TODO better source location
  27704     const indexable_ty = sema.typeOf(indexable);
  27705     const pt = sema.pt;
  27706     const zcu = pt.zcu;
  27707 
  27708     try sema.checkIndexable(block, src, indexable_ty);
  27709 
  27710     switch (indexable_ty.ptrSize(zcu)) {
  27711         .slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  27712         .many, .c => {
  27713             const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  27714             const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  27715             ct: {
  27716                 const ptr_val = maybe_ptr_val orelse break :ct;
  27717                 const index_val = maybe_index_val orelse break :ct;
  27718                 const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  27719                 const elem_ptr = try ptr_val.ptrElem(index, pt);
  27720                 return Air.internedToRef(elem_ptr.toIntern());
  27721             }
  27722 
  27723             try sema.checkLogicalPtrOperation(block, src, indexable_ty);
  27724             const result_ty = try indexable_ty.elemPtrType(null, pt);
  27725 
  27726             try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_ty, indexable_src);
  27727             try sema.validateRuntimeValue(block, indexable_src, indexable);
  27728 
  27729             if (!try result_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) {
  27730                 // zero-bit child type; just bitcast the pointer
  27731                 return block.addBitCast(result_ty, indexable);
  27732             }
  27733 
  27734             return block.addPtrElemPtr(indexable, elem_index, result_ty);
  27735         },
  27736         .one => {
  27737             const child_ty = indexable_ty.childType(zcu);
  27738             const elem_ptr = switch (child_ty.zigTypeTag(zcu)) {
  27739                 .array, .vector => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety),
  27740                 .@"struct" => blk: {
  27741                     assert(child_ty.isTuple(zcu));
  27742                     const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
  27743                     const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt));
  27744                     break :blk try sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
  27745                 },
  27746                 else => unreachable, // Guaranteed by checkIndexable
  27747             };
  27748             try sema.checkKnownAllocPtr(block, indexable, elem_ptr);
  27749             return elem_ptr;
  27750         },
  27751     }
  27752 }
  27753 
  27754 fn elemVal(
  27755     sema: *Sema,
  27756     block: *Block,
  27757     src: LazySrcLoc,
  27758     indexable: Air.Inst.Ref,
  27759     elem_index_uncasted: Air.Inst.Ref,
  27760     elem_index_src: LazySrcLoc,
  27761     oob_safety: bool,
  27762 ) CompileError!Air.Inst.Ref {
  27763     const indexable_src = src; // TODO better source location
  27764     const indexable_ty = sema.typeOf(indexable);
  27765     const pt = sema.pt;
  27766     const zcu = pt.zcu;
  27767 
  27768     try sema.checkIndexable(block, src, indexable_ty);
  27769 
  27770     // TODO in case of a vector of pointers, we need to detect whether the element
  27771     // index is a scalar or vector instead of unconditionally casting to usize.
  27772     const elem_index = try sema.coerce(block, .usize, elem_index_uncasted, elem_index_src);
  27773 
  27774     switch (indexable_ty.zigTypeTag(zcu)) {
  27775         .pointer => switch (indexable_ty.ptrSize(zcu)) {
  27776             .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  27777             .many, .c => {
  27778                 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
  27779                 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  27780                 const elem_ty = indexable_ty.elemType2(zcu);
  27781 
  27782                 ct: {
  27783                     const indexable_val = maybe_indexable_val orelse break :ct;
  27784                     const index_val = maybe_index_val orelse break :ct;
  27785                     const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  27786                     const many_ptr_ty = try pt.manyConstPtrType(elem_ty);
  27787                     const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty);
  27788                     const elem_ptr_ty = try pt.singleConstPtrType(elem_ty);
  27789                     const elem_ptr_val = try many_ptr_val.ptrElem(index, pt);
  27790                     const elem_val = try sema.pointerDeref(block, indexable_src, elem_ptr_val, elem_ptr_ty) orelse break :ct;
  27791                     return Air.internedToRef((try pt.getCoerced(elem_val, elem_ty)).toIntern());
  27792                 }
  27793 
  27794                 if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| {
  27795                     return Air.internedToRef(elem_only_value.toIntern());
  27796                 }
  27797 
  27798                 try sema.checkLogicalPtrOperation(block, src, indexable_ty);
  27799                 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
  27800             },
  27801             .one => {
  27802                 arr_sent: {
  27803                     const inner_ty = indexable_ty.childType(zcu);
  27804                     if (inner_ty.zigTypeTag(zcu) != .array) break :arr_sent;
  27805                     const sentinel = inner_ty.sentinel(zcu) orelse break :arr_sent;
  27806                     const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
  27807                     const index = try sema.usizeCast(block, src, try index_val.toUnsignedIntSema(pt));
  27808                     if (index != inner_ty.arrayLen(zcu)) break :arr_sent;
  27809                     return Air.internedToRef(sentinel.toIntern());
  27810                 }
  27811                 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
  27812                 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
  27813             },
  27814         },
  27815         .array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
  27816         .vector => {
  27817             // TODO: If the index is a vector, the result should be a vector.
  27818             return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety);
  27819         },
  27820         .@"struct" => {
  27821             // Tuple field access.
  27822             const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
  27823             const index: u32 = @intCast(try index_val.toUnsignedIntSema(pt));
  27824             return sema.tupleField(block, indexable_src, indexable, elem_index_src, index);
  27825         },
  27826         else => unreachable,
  27827     }
  27828 }
  27829 
  27830 fn validateRuntimeElemAccess(
  27831     sema: *Sema,
  27832     block: *Block,
  27833     elem_index_src: LazySrcLoc,
  27834     elem_ty: Type,
  27835     parent_ty: Type,
  27836     parent_src: LazySrcLoc,
  27837 ) CompileError!void {
  27838     const pt = sema.pt;
  27839     const zcu = pt.zcu;
  27840 
  27841     if (try elem_ty.comptimeOnlySema(sema.pt)) {
  27842         const msg = msg: {
  27843             const msg = try sema.errMsg(
  27844                 elem_index_src,
  27845                 "values of type '{f}' must be comptime-known, but index value is runtime-known",
  27846                 .{parent_ty.fmt(sema.pt)},
  27847             );
  27848             errdefer msg.destroy(sema.gpa);
  27849 
  27850             try sema.explainWhyTypeIsComptime(msg, parent_src, parent_ty);
  27851 
  27852             break :msg msg;
  27853         };
  27854         return sema.failWithOwnedErrorMsg(block, msg);
  27855     }
  27856 
  27857     if (zcu.intern_pool.indexToKey(parent_ty.toIntern()) == .ptr_type) {
  27858         const target = zcu.getTarget();
  27859         const as = parent_ty.ptrAddressSpace(zcu);
  27860         if (target_util.arePointersLogical(target, as)) {
  27861             return sema.fail(block, elem_index_src, "cannot access element of logical pointer '{f}'", .{parent_ty.fmt(pt)});
  27862         }
  27863     }
  27864 }
  27865 
  27866 fn tupleFieldPtr(
  27867     sema: *Sema,
  27868     block: *Block,
  27869     tuple_ptr_src: LazySrcLoc,
  27870     tuple_ptr: Air.Inst.Ref,
  27871     field_index_src: LazySrcLoc,
  27872     field_index: u32,
  27873     init: bool,
  27874 ) CompileError!Air.Inst.Ref {
  27875     const pt = sema.pt;
  27876     const zcu = pt.zcu;
  27877     const tuple_ptr_ty = sema.typeOf(tuple_ptr);
  27878     const tuple_ptr_info = tuple_ptr_ty.ptrInfo(zcu);
  27879     const tuple_ty: Type = .fromInterned(tuple_ptr_info.child);
  27880     try tuple_ty.resolveFields(pt);
  27881     const field_count = tuple_ty.structFieldCount(zcu);
  27882 
  27883     if (field_count == 0) {
  27884         return sema.fail(block, tuple_ptr_src, "indexing into empty tuple is not allowed", .{});
  27885     }
  27886 
  27887     if (field_index >= field_count) {
  27888         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  27889             field_index, field_count,
  27890         });
  27891     }
  27892 
  27893     const field_ty = tuple_ty.fieldType(field_index, zcu);
  27894     const ptr_field_ty = try pt.ptrTypeSema(.{
  27895         .child = field_ty.toIntern(),
  27896         .flags = .{
  27897             .is_const = tuple_ptr_info.flags.is_const,
  27898             .is_volatile = tuple_ptr_info.flags.is_volatile,
  27899             .address_space = tuple_ptr_info.flags.address_space,
  27900             .alignment = a: {
  27901                 if (tuple_ptr_info.flags.alignment == .none) break :a .none;
  27902                 // The tuple pointer isn't naturally aligned, so the field pointer might be underaligned.
  27903                 const tuple_align = tuple_ptr_info.flags.alignment;
  27904                 const field_align = try field_ty.abiAlignmentSema(pt);
  27905                 break :a tuple_align.min(field_align);
  27906             },
  27907         },
  27908     });
  27909 
  27910     if (tuple_ty.structFieldIsComptime(field_index, zcu))
  27911         try tuple_ty.resolveStructFieldInits(pt);
  27912 
  27913     if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_val| {
  27914         return Air.internedToRef((try pt.intern(.{ .ptr = .{
  27915             .ty = ptr_field_ty.toIntern(),
  27916             .base_addr = .{ .comptime_field = default_val.toIntern() },
  27917             .byte_offset = 0,
  27918         } })));
  27919     }
  27920 
  27921     if (try sema.resolveValue(tuple_ptr)) |tuple_ptr_val| {
  27922         const field_ptr_val = try tuple_ptr_val.ptrField(field_index, pt);
  27923         return Air.internedToRef(field_ptr_val.toIntern());
  27924     }
  27925 
  27926     if (!init) {
  27927         try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_ptr_src);
  27928     }
  27929 
  27930     return block.addStructFieldPtr(tuple_ptr, field_index, ptr_field_ty);
  27931 }
  27932 
  27933 fn tupleField(
  27934     sema: *Sema,
  27935     block: *Block,
  27936     tuple_src: LazySrcLoc,
  27937     tuple: Air.Inst.Ref,
  27938     field_index_src: LazySrcLoc,
  27939     field_index: u32,
  27940 ) CompileError!Air.Inst.Ref {
  27941     const pt = sema.pt;
  27942     const zcu = pt.zcu;
  27943     const tuple_ty = sema.typeOf(tuple);
  27944     try tuple_ty.resolveFields(pt);
  27945     const field_count = tuple_ty.structFieldCount(zcu);
  27946 
  27947     if (field_count == 0) {
  27948         return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});
  27949     }
  27950 
  27951     if (field_index >= field_count) {
  27952         return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
  27953             field_index, field_count,
  27954         });
  27955     }
  27956 
  27957     const field_ty = tuple_ty.fieldType(field_index, zcu);
  27958 
  27959     if (tuple_ty.structFieldIsComptime(field_index, zcu))
  27960         try tuple_ty.resolveStructFieldInits(pt);
  27961     if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| {
  27962         return Air.internedToRef(default_value.toIntern()); // comptime field
  27963     }
  27964 
  27965     if (try sema.resolveValue(tuple)) |tuple_val| {
  27966         if (tuple_val.isUndef(zcu)) return pt.undefRef(field_ty);
  27967         return Air.internedToRef((try tuple_val.fieldValue(pt, field_index)).toIntern());
  27968     }
  27969 
  27970     try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_ty, tuple_src);
  27971 
  27972     try field_ty.resolveLayout(pt);
  27973     return block.addStructFieldVal(tuple, field_index, field_ty);
  27974 }
  27975 
  27976 fn elemValArray(
  27977     sema: *Sema,
  27978     block: *Block,
  27979     src: LazySrcLoc,
  27980     array_src: LazySrcLoc,
  27981     array: Air.Inst.Ref,
  27982     elem_index_src: LazySrcLoc,
  27983     elem_index: Air.Inst.Ref,
  27984     oob_safety: bool,
  27985 ) CompileError!Air.Inst.Ref {
  27986     const pt = sema.pt;
  27987     const zcu = pt.zcu;
  27988     const array_ty = sema.typeOf(array);
  27989     const array_sent = array_ty.sentinel(zcu);
  27990     const array_len = array_ty.arrayLen(zcu);
  27991     const array_len_s = array_len + @intFromBool(array_sent != null);
  27992     const elem_ty = array_ty.childType(zcu);
  27993 
  27994     if (array_len_s == 0) {
  27995         return sema.fail(block, array_src, "indexing into empty array is not allowed", .{});
  27996     }
  27997 
  27998     const maybe_undef_array_val = try sema.resolveValue(array);
  27999     // index must be defined since it can access out of bounds
  28000     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  28001 
  28002     if (maybe_index_val) |index_val| {
  28003         const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  28004         if (array_sent) |s| {
  28005             if (index == array_len) {
  28006                 return Air.internedToRef(s.toIntern());
  28007             }
  28008         }
  28009         if (index >= array_len_s) {
  28010             const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else "";
  28011             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  28012         }
  28013     }
  28014     if (maybe_undef_array_val) |array_val| {
  28015         if (array_val.isUndef(zcu)) {
  28016             return pt.undefRef(elem_ty);
  28017         }
  28018         if (maybe_index_val) |index_val| {
  28019             const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  28020             const elem_val = try array_val.elemValue(pt, index);
  28021             return Air.internedToRef(elem_val.toIntern());
  28022         }
  28023     }
  28024 
  28025     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_ty, array_src);
  28026     try sema.validateRuntimeValue(block, array_src, array);
  28027 
  28028     if (oob_safety and block.wantSafety()) {
  28029         // Runtime check is only needed if unable to comptime check.
  28030         if (maybe_index_val == null) {
  28031             const len_inst = try pt.intRef(.usize, array_len);
  28032             const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
  28033             try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
  28034         }
  28035     }
  28036 
  28037     if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_val|
  28038         return Air.internedToRef(elem_val.toIntern());
  28039 
  28040     return block.addBinOp(.array_elem_val, array, elem_index);
  28041 }
  28042 
  28043 fn elemPtrArray(
  28044     sema: *Sema,
  28045     block: *Block,
  28046     src: LazySrcLoc,
  28047     array_ptr_src: LazySrcLoc,
  28048     array_ptr: Air.Inst.Ref,
  28049     elem_index_src: LazySrcLoc,
  28050     elem_index: Air.Inst.Ref,
  28051     init: bool,
  28052     oob_safety: bool,
  28053 ) CompileError!Air.Inst.Ref {
  28054     const pt = sema.pt;
  28055     const zcu = pt.zcu;
  28056     const array_ptr_ty = sema.typeOf(array_ptr);
  28057     const array_ty = array_ptr_ty.childType(zcu);
  28058     const array_sent = array_ty.sentinel(zcu) != null;
  28059     const array_len = array_ty.arrayLen(zcu);
  28060     const array_len_s = array_len + @intFromBool(array_sent);
  28061 
  28062     if (array_len_s == 0) {
  28063         return sema.fail(block, array_ptr_src, "indexing into empty array is not allowed", .{});
  28064     }
  28065 
  28066     const maybe_undef_array_ptr_val = try sema.resolveValue(array_ptr);
  28067     // The index must not be undefined since it can be out of bounds.
  28068     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  28069         const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt));
  28070         if (index >= array_len_s) {
  28071             const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
  28072             return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
  28073         }
  28074         break :o index;
  28075     } else null;
  28076 
  28077     const elem_ptr_ty = try array_ptr_ty.elemPtrType(offset, pt);
  28078 
  28079     if (maybe_undef_array_ptr_val) |array_ptr_val| {
  28080         if (array_ptr_val.isUndef(zcu)) {
  28081             return pt.undefRef(elem_ptr_ty);
  28082         }
  28083         if (offset) |index| {
  28084             const elem_ptr = try array_ptr_val.ptrElem(index, pt);
  28085             return Air.internedToRef(elem_ptr.toIntern());
  28086         }
  28087     }
  28088 
  28089     if (!init) {
  28090         try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.elemType2(zcu), array_ty, array_ptr_src);
  28091         try sema.validateRuntimeValue(block, array_ptr_src, array_ptr);
  28092     }
  28093 
  28094     // Runtime check is only needed if unable to comptime check.
  28095     if (oob_safety and block.wantSafety() and offset == null) {
  28096         const len_inst = try pt.intRef(.usize, array_len);
  28097         const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
  28098         try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
  28099     }
  28100 
  28101     return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
  28102 }
  28103 
  28104 fn elemValSlice(
  28105     sema: *Sema,
  28106     block: *Block,
  28107     src: LazySrcLoc,
  28108     slice_src: LazySrcLoc,
  28109     slice: Air.Inst.Ref,
  28110     elem_index_src: LazySrcLoc,
  28111     elem_index: Air.Inst.Ref,
  28112     oob_safety: bool,
  28113 ) CompileError!Air.Inst.Ref {
  28114     const pt = sema.pt;
  28115     const zcu = pt.zcu;
  28116     const slice_ty = sema.typeOf(slice);
  28117     const slice_sent = slice_ty.sentinel(zcu) != null;
  28118     const elem_ty = slice_ty.elemType2(zcu);
  28119     var runtime_src = slice_src;
  28120 
  28121     // slice must be defined since it can dereferenced as null
  28122     const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
  28123     // index must be defined since it can index out of bounds
  28124     const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
  28125 
  28126     if (maybe_slice_val) |slice_val| {
  28127         runtime_src = elem_index_src;
  28128         const slice_len = try slice_val.sliceLen(pt);
  28129         const slice_len_s = slice_len + @intFromBool(slice_sent);
  28130         if (slice_len_s == 0) {
  28131             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  28132         }
  28133         if (maybe_index_val) |index_val| {
  28134             const index: usize = @intCast(try index_val.toUnsignedIntSema(pt));
  28135             if (index >= slice_len_s) {
  28136                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  28137                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  28138             }
  28139             const elem_ptr_ty = try slice_ty.elemPtrType(index, pt);
  28140             const elem_ptr_val = try slice_val.ptrElem(index, pt);
  28141             if (try sema.pointerDeref(block, slice_src, elem_ptr_val, elem_ptr_ty)) |elem_val| {
  28142                 return Air.internedToRef(elem_val.toIntern());
  28143             }
  28144             runtime_src = slice_src;
  28145         }
  28146     }
  28147 
  28148     if (try sema.typeHasOnePossibleValue(elem_ty)) |elem_only_value| {
  28149         return Air.internedToRef(elem_only_value.toIntern());
  28150     }
  28151 
  28152     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_ty, slice_src);
  28153     try sema.validateRuntimeValue(block, slice_src, slice);
  28154 
  28155     if (oob_safety and block.wantSafety()) {
  28156         const len_inst = if (maybe_slice_val) |slice_val|
  28157             try pt.intRef(.usize, try slice_val.sliceLen(pt))
  28158         else
  28159             try block.addTyOp(.slice_len, .usize, slice);
  28160         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  28161         try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
  28162     }
  28163     return block.addBinOp(.slice_elem_val, slice, elem_index);
  28164 }
  28165 
  28166 fn elemPtrSlice(
  28167     sema: *Sema,
  28168     block: *Block,
  28169     src: LazySrcLoc,
  28170     slice_src: LazySrcLoc,
  28171     slice: Air.Inst.Ref,
  28172     elem_index_src: LazySrcLoc,
  28173     elem_index: Air.Inst.Ref,
  28174     oob_safety: bool,
  28175 ) CompileError!Air.Inst.Ref {
  28176     const pt = sema.pt;
  28177     const zcu = pt.zcu;
  28178     const slice_ty = sema.typeOf(slice);
  28179     const slice_sent = slice_ty.sentinel(zcu) != null;
  28180 
  28181     const maybe_undef_slice_val = try sema.resolveValue(slice);
  28182     // The index must not be undefined since it can be out of bounds.
  28183     const offset: ?usize = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
  28184         const index = try sema.usizeCast(block, elem_index_src, try index_val.toUnsignedIntSema(pt));
  28185         break :o index;
  28186     } else null;
  28187 
  28188     const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt);
  28189 
  28190     if (maybe_undef_slice_val) |slice_val| {
  28191         if (slice_val.isUndef(zcu)) {
  28192             return pt.undefRef(elem_ptr_ty);
  28193         }
  28194         const slice_len = try slice_val.sliceLen(pt);
  28195         const slice_len_s = slice_len + @intFromBool(slice_sent);
  28196         if (slice_len_s == 0) {
  28197             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
  28198         }
  28199         if (offset) |index| {
  28200             if (index >= slice_len_s) {
  28201                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
  28202                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
  28203             }
  28204             const elem_ptr_val = try slice_val.ptrElem(index, pt);
  28205             return Air.internedToRef(elem_ptr_val.toIntern());
  28206         }
  28207     }
  28208 
  28209     try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_ty, slice_src);
  28210     try sema.validateRuntimeValue(block, slice_src, slice);
  28211 
  28212     if (oob_safety and block.wantSafety()) {
  28213         const len_inst = len: {
  28214             if (maybe_undef_slice_val) |slice_val|
  28215                 if (!slice_val.isUndef(zcu))
  28216                     break :len try pt.intRef(.usize, try slice_val.sliceLen(pt));
  28217             break :len try block.addTyOp(.slice_len, .usize, slice);
  28218         };
  28219         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
  28220         try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
  28221     }
  28222     if (!try slice_ty.childType(zcu).hasRuntimeBitsIgnoreComptimeSema(pt)) {
  28223         // zero-bit child type; just extract the pointer and bitcast it
  28224         const slice_ptr = try block.addTyOp(.slice_ptr, slice_ty.slicePtrFieldType(zcu), slice);
  28225         return block.addBitCast(elem_ptr_ty, slice_ptr);
  28226     }
  28227     return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
  28228 }
  28229 
  28230 pub fn coerce(
  28231     sema: *Sema,
  28232     block: *Block,
  28233     dest_ty_unresolved: Type,
  28234     inst: Air.Inst.Ref,
  28235     inst_src: LazySrcLoc,
  28236 ) CompileError!Air.Inst.Ref {
  28237     return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
  28238         error.NotCoercible => unreachable,
  28239         else => |e| return e,
  28240     };
  28241 }
  28242 
  28243 const CoersionError = CompileError || error{
  28244     /// When coerce is called recursively, this error should be returned instead of using `fail`
  28245     /// to ensure correct types in compile errors.
  28246     NotCoercible,
  28247 };
  28248 
  28249 const CoerceOpts = struct {
  28250     /// Should coerceExtra emit error messages.
  28251     report_err: bool = true,
  28252     /// Ignored if `report_err == false`.
  28253     is_ret: bool = false,
  28254     /// Should coercion to comptime_int emit an error message.
  28255     no_cast_to_comptime_int: bool = false,
  28256 
  28257     param_src: struct {
  28258         func_inst: Air.Inst.Ref = .none,
  28259         param_i: u32 = undefined,
  28260 
  28261         fn get(info: @This(), sema: *Sema) !?LazySrcLoc {
  28262             if (info.func_inst == .none) return null;
  28263             const func_inst = try sema.funcDeclSrcInst(info.func_inst) orelse return null;
  28264             return .{
  28265                 .base_node_inst = func_inst,
  28266                 .offset = .{ .fn_proto_param_type = .{
  28267                     .fn_proto_node_offset = .zero,
  28268                     .param_index = info.param_i,
  28269                 } },
  28270             };
  28271         }
  28272     } = .{ .func_inst = .none, .param_i = undefined },
  28273 };
  28274 
  28275 fn coerceExtra(
  28276     sema: *Sema,
  28277     block: *Block,
  28278     dest_ty: Type,
  28279     inst: Air.Inst.Ref,
  28280     inst_src: LazySrcLoc,
  28281     opts: CoerceOpts,
  28282 ) CoersionError!Air.Inst.Ref {
  28283     if (dest_ty.isGenericPoison()) return inst;
  28284     const pt = sema.pt;
  28285     const zcu = pt.zcu;
  28286     const ip = &zcu.intern_pool;
  28287     const dest_ty_src = inst_src; // TODO better source location
  28288     try dest_ty.resolveFields(pt);
  28289     const inst_ty = sema.typeOf(inst);
  28290     try inst_ty.resolveFields(pt);
  28291     const target = zcu.getTarget();
  28292     // If the types are the same, we can return the operand.
  28293     if (dest_ty.eql(inst_ty, zcu))
  28294         return inst;
  28295 
  28296     const maybe_inst_val = try sema.resolveValue(inst);
  28297 
  28298     var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
  28299     if (in_memory_result == .ok) {
  28300         if (maybe_inst_val) |val| {
  28301             return sema.coerceInMemory(val, dest_ty);
  28302         }
  28303         try sema.requireRuntimeBlock(block, inst_src, null);
  28304         const new_val = try block.addBitCast(dest_ty, inst);
  28305         try sema.checkKnownAllocPtr(block, inst, new_val);
  28306         return new_val;
  28307     }
  28308 
  28309     switch (dest_ty.zigTypeTag(zcu)) {
  28310         .optional => optional: {
  28311             if (maybe_inst_val) |val| {
  28312                 // undefined sets the optional bit also to undefined.
  28313                 if (val.toIntern() == .undef) {
  28314                     return pt.undefRef(dest_ty);
  28315                 }
  28316 
  28317                 // null to ?T
  28318                 if (val.toIntern() == .null_value) {
  28319                     return Air.internedToRef((try pt.intern(.{ .opt = .{
  28320                         .ty = dest_ty.toIntern(),
  28321                         .val = .none,
  28322                     } })));
  28323                 }
  28324             }
  28325 
  28326             // cast from ?*T and ?[*]T to ?*anyopaque
  28327             // but don't do it if the source type is a double pointer
  28328             if (dest_ty.isPtrLikeOptional(zcu) and
  28329                 dest_ty.elemType2(zcu).toIntern() == .anyopaque_type and
  28330                 inst_ty.isPtrAtRuntime(zcu))
  28331             anyopaque_check: {
  28332                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional;
  28333                 const elem_ty = inst_ty.elemType2(zcu);
  28334                 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) {
  28335                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  28336                         .actual = inst_ty,
  28337                         .wanted = dest_ty,
  28338                     } };
  28339                     break :optional;
  28340                 }
  28341                 // Let the logic below handle wrapping the optional now that
  28342                 // it has been checked to correctly coerce.
  28343                 if (!inst_ty.isPtrLikeOptional(zcu)) break :anyopaque_check;
  28344                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28345             }
  28346 
  28347             // T to ?T
  28348             const child_type = dest_ty.optionalChild(zcu);
  28349             const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  28350                 error.NotCoercible => {
  28351                     if (in_memory_result == .no_match) {
  28352                         // Try to give more useful notes
  28353                         in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
  28354                     }
  28355                     break :optional;
  28356                 },
  28357                 else => |e| return e,
  28358             };
  28359             return try sema.wrapOptional(block, dest_ty, intermediate, inst_src);
  28360         },
  28361         .pointer => pointer: {
  28362             const dest_info = dest_ty.ptrInfo(zcu);
  28363 
  28364             // Function body to function pointer.
  28365             if (inst_ty.zigTypeTag(zcu) == .@"fn") {
  28366                 const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
  28367                 const fn_nav = switch (zcu.intern_pool.indexToKey(fn_val.toIntern())) {
  28368                     .func => |f| f.owner_nav,
  28369                     .@"extern" => |e| e.owner_nav,
  28370                     else => unreachable,
  28371                 };
  28372                 const inst_as_ptr = try sema.analyzeNavRef(block, inst_src, fn_nav);
  28373                 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
  28374             }
  28375 
  28376             // *T to *[1]T
  28377             single_item: {
  28378                 if (dest_info.flags.size != .one) break :single_item;
  28379                 if (!inst_ty.isSinglePointer(zcu)) break :single_item;
  28380                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  28381                 const ptr_elem_ty = inst_ty.childType(zcu);
  28382                 const array_ty: Type = .fromInterned(dest_info.child);
  28383                 if (array_ty.zigTypeTag(zcu) != .array) break :single_item;
  28384                 const array_elem_ty = array_ty.childType(zcu);
  28385                 if (array_ty.arrayLen(zcu) != 1) break :single_item;
  28386                 const dest_is_mut = !dest_info.flags.is_const;
  28387                 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) {
  28388                     .ok => {},
  28389                     else => break :single_item,
  28390                 }
  28391                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28392             }
  28393 
  28394             // Coercions where the source is a single pointer to an array.
  28395             src_array_ptr: {
  28396                 if (!inst_ty.isSinglePointer(zcu)) break :src_array_ptr;
  28397                 if (dest_info.flags.size == .one) break :src_array_ptr; // `*[n]T` -> `*T` isn't valid
  28398                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  28399                 const array_ty = inst_ty.childType(zcu);
  28400                 if (array_ty.zigTypeTag(zcu) != .array) break :src_array_ptr;
  28401                 const array_elem_type = array_ty.childType(zcu);
  28402                 const dest_is_mut = !dest_info.flags.is_const;
  28403 
  28404                 const dst_elem_type: Type = .fromInterned(dest_info.child);
  28405                 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);
  28406                 switch (elem_res) {
  28407                     .ok => {},
  28408                     else => {
  28409                         in_memory_result = .{ .ptr_child = .{
  28410                             .child = try elem_res.dupe(sema.arena),
  28411                             .actual = array_elem_type,
  28412                             .wanted = dst_elem_type,
  28413                         } };
  28414                         break :src_array_ptr;
  28415                     },
  28416                 }
  28417 
  28418                 if (dest_info.sentinel != .none) {
  28419                     if (array_ty.sentinel(zcu)) |inst_sent| {
  28420                         if (Air.internedToRef(dest_info.sentinel) !=
  28421                             try sema.coerceInMemory(inst_sent, dst_elem_type))
  28422                         {
  28423                             in_memory_result = .{ .ptr_sentinel = .{
  28424                                 .actual = inst_sent,
  28425                                 .wanted = Value.fromInterned(dest_info.sentinel),
  28426                                 .ty = dst_elem_type,
  28427                             } };
  28428                             break :src_array_ptr;
  28429                         }
  28430                     } else {
  28431                         in_memory_result = .{ .ptr_sentinel = .{
  28432                             .actual = Value.@"unreachable",
  28433                             .wanted = Value.fromInterned(dest_info.sentinel),
  28434                             .ty = dst_elem_type,
  28435                         } };
  28436                         break :src_array_ptr;
  28437                     }
  28438                 }
  28439 
  28440                 switch (dest_info.flags.size) {
  28441                     .slice => {
  28442                         // *[N]T to []T
  28443                         return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
  28444                     },
  28445                     .c => {
  28446                         // *[N]T to [*c]T
  28447                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28448                     },
  28449                     .many => {
  28450                         // *[N]T to [*]T
  28451                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28452                     },
  28453                     .one => unreachable, // early exit at top of block
  28454                 }
  28455             }
  28456 
  28457             // coercion from C pointer
  28458             if (inst_ty.isCPtr(zcu)) src_c_ptr: {
  28459                 if (dest_info.flags.size == .slice) break :src_c_ptr;
  28460                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr;
  28461                 // In this case we must add a safety check because the C pointer
  28462                 // could be null.
  28463                 const src_elem_ty = inst_ty.childType(zcu);
  28464                 const dest_is_mut = !dest_info.flags.is_const;
  28465                 const dst_elem_type: Type = .fromInterned(dest_info.child);
  28466                 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, maybe_inst_val)) {
  28467                     .ok => {},
  28468                     else => break :src_c_ptr,
  28469                 }
  28470                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28471             }
  28472 
  28473             // cast from *T and [*]T to *anyopaque
  28474             // but don't do it if the source type is a double pointer
  28475             if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(zcu) == .pointer) to_anyopaque: {
  28476                 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
  28477                 const elem_ty = inst_ty.elemType2(zcu);
  28478                 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) {
  28479                     in_memory_result = .{ .double_ptr_to_anyopaque = .{
  28480                         .actual = inst_ty,
  28481                         .wanted = dest_ty,
  28482                     } };
  28483                     break :pointer;
  28484                 }
  28485                 if (dest_ty.isSlice(zcu)) break :to_anyopaque;
  28486                 if (inst_ty.isSlice(zcu)) {
  28487                     in_memory_result = .{ .slice_to_anyopaque = .{
  28488                         .actual = inst_ty,
  28489                         .wanted = dest_ty,
  28490                     } };
  28491                     break :pointer;
  28492                 }
  28493                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28494             }
  28495 
  28496             switch (dest_info.flags.size) {
  28497                 // coercion to C pointer
  28498                 .c => switch (inst_ty.zigTypeTag(zcu)) {
  28499                     .null => return Air.internedToRef(try pt.intern(.{ .ptr = .{
  28500                         .ty = dest_ty.toIntern(),
  28501                         .base_addr = .int,
  28502                         .byte_offset = 0,
  28503                     } })),
  28504                     .comptime_int => {
  28505                         const addr = sema.coerceExtra(block, .usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  28506                             error.NotCoercible => break :pointer,
  28507                             else => |e| return e,
  28508                         };
  28509                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  28510                     },
  28511                     .int => {
  28512                         const ptr_size_ty: Type = switch (inst_ty.intInfo(zcu).signedness) {
  28513                             .signed => .isize,
  28514                             .unsigned => .usize,
  28515                         };
  28516                         const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
  28517                             error.NotCoercible => {
  28518                                 // Try to give more useful notes
  28519                                 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
  28520                                 break :pointer;
  28521                             },
  28522                             else => |e| return e,
  28523                         };
  28524                         return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
  28525                     },
  28526                     .pointer => p: {
  28527                         if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  28528                         const inst_info = inst_ty.ptrInfo(zcu);
  28529                         switch (try sema.coerceInMemoryAllowed(
  28530                             block,
  28531                             .fromInterned(dest_info.child),
  28532                             .fromInterned(inst_info.child),
  28533                             !dest_info.flags.is_const,
  28534                             target,
  28535                             dest_ty_src,
  28536                             inst_src,
  28537                             maybe_inst_val,
  28538                         )) {
  28539                             .ok => {},
  28540                             else => break :p,
  28541                         }
  28542                         if (inst_info.flags.size == .slice) {
  28543                             assert(dest_info.sentinel == .none);
  28544                             if (inst_info.sentinel == .none or
  28545                                 inst_info.sentinel != (try pt.intValue(.fromInterned(inst_info.child), 0)).toIntern())
  28546                                 break :p;
  28547 
  28548                             const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  28549                             return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  28550                         }
  28551                         return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
  28552                     },
  28553                     else => {},
  28554                 },
  28555                 .one => {},
  28556                 .slice => to_slice: {
  28557                     if (inst_ty.zigTypeTag(zcu) == .array) {
  28558                         return sema.fail(
  28559                             block,
  28560                             inst_src,
  28561                             "array literal requires address-of operator (&) to coerce to slice type '{f}'",
  28562                             .{dest_ty.fmt(pt)},
  28563                         );
  28564                     }
  28565 
  28566                     if (!inst_ty.isSinglePointer(zcu)) break :to_slice;
  28567                     const inst_child_ty = inst_ty.childType(zcu);
  28568                     if (!inst_child_ty.isTuple(zcu)) break :to_slice;
  28569 
  28570                     // empty tuple to zero-length slice
  28571                     // note that this allows coercing to a mutable slice.
  28572                     if (inst_child_ty.structFieldCount(zcu) == 0) {
  28573                         const align_val = try dest_ty.ptrAlignmentSema(pt);
  28574                         return Air.internedToRef(try pt.intern(.{ .slice = .{
  28575                             .ty = dest_ty.toIntern(),
  28576                             .ptr = try pt.intern(.{ .ptr = .{
  28577                                 .ty = dest_ty.slicePtrFieldType(zcu).toIntern(),
  28578                                 .base_addr = .int,
  28579                                 .byte_offset = align_val.toByteUnits().?,
  28580                             } }),
  28581                             .len = .zero_usize,
  28582                         } }));
  28583                     }
  28584 
  28585                     // pointer to tuple to slice
  28586                     if (!dest_info.flags.is_const) {
  28587                         const err_msg = err_msg: {
  28588                             const err_msg = try sema.errMsg(inst_src, "cannot cast pointer to tuple to '{f}'", .{dest_ty.fmt(pt)});
  28589                             errdefer err_msg.destroy(sema.gpa);
  28590                             try sema.errNote(dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{});
  28591                             break :err_msg err_msg;
  28592                         };
  28593                         return sema.failWithOwnedErrorMsg(block, err_msg);
  28594                     }
  28595                     return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
  28596                 },
  28597                 .many => p: {
  28598                     if (!inst_ty.isSlice(zcu)) break :p;
  28599                     if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
  28600                     const inst_info = inst_ty.ptrInfo(zcu);
  28601 
  28602                     switch (try sema.coerceInMemoryAllowed(
  28603                         block,
  28604                         .fromInterned(dest_info.child),
  28605                         .fromInterned(inst_info.child),
  28606                         !dest_info.flags.is_const,
  28607                         target,
  28608                         dest_ty_src,
  28609                         inst_src,
  28610                         maybe_inst_val,
  28611                     )) {
  28612                         .ok => {},
  28613                         else => break :p,
  28614                     }
  28615 
  28616                     if (dest_info.sentinel == .none or inst_info.sentinel == .none or
  28617                         Air.internedToRef(dest_info.sentinel) !=
  28618                             try sema.coerceInMemory(Value.fromInterned(inst_info.sentinel), .fromInterned(dest_info.child)))
  28619                         break :p;
  28620 
  28621                     const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
  28622                     return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
  28623                 },
  28624             }
  28625         },
  28626         .int, .comptime_int => switch (inst_ty.zigTypeTag(zcu)) {
  28627             .float, .comptime_float => float: {
  28628                 const val = maybe_inst_val orelse {
  28629                     if (dest_ty.zigTypeTag(zcu) == .comptime_int) {
  28630                         if (!opts.report_err) return error.NotCoercible;
  28631                         return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int });
  28632                     }
  28633                     break :float;
  28634                 };
  28635                 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact);
  28636                 return Air.internedToRef(result_val.toIntern());
  28637             },
  28638             .int, .comptime_int => {
  28639                 if (maybe_inst_val) |val| {
  28640                     // comptime-known integer to other number
  28641                     if (!(try sema.intFitsInType(val, dest_ty, null))) {
  28642                         if (!opts.report_err) return error.NotCoercible;
  28643                         return sema.fail(block, inst_src, "type '{f}' cannot represent integer value '{f}'", .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) });
  28644                     }
  28645                     return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  28646                         .undef => try pt.undefRef(dest_ty),
  28647                         .int => |int| Air.internedToRef(
  28648                             try zcu.intern_pool.getCoercedInts(zcu.gpa, pt.tid, int, dest_ty.toIntern()),
  28649                         ),
  28650                         else => unreachable,
  28651                     };
  28652                 }
  28653                 if (dest_ty.zigTypeTag(zcu) == .comptime_int) {
  28654                     if (!opts.report_err) return error.NotCoercible;
  28655                     if (opts.no_cast_to_comptime_int) return inst;
  28656                     return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int });
  28657                 }
  28658 
  28659                 // integer widening
  28660                 const dst_info = dest_ty.intInfo(zcu);
  28661                 const src_info = inst_ty.intInfo(zcu);
  28662                 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
  28663                     // small enough unsigned ints can get casted to large enough signed ints
  28664                     (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
  28665                 {
  28666                     try sema.requireRuntimeBlock(block, inst_src, null);
  28667                     return block.addTyOp(.intcast, dest_ty, inst);
  28668                 }
  28669             },
  28670             else => {},
  28671         },
  28672         .float, .comptime_float => switch (inst_ty.zigTypeTag(zcu)) {
  28673             .comptime_float => {
  28674                 const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
  28675                 const result_val = try val.floatCast(dest_ty, pt);
  28676                 return Air.internedToRef(result_val.toIntern());
  28677             },
  28678             .float => {
  28679                 if (maybe_inst_val) |val| {
  28680                     const result_val = try val.floatCast(dest_ty, pt);
  28681                     if (!val.eql(try result_val.floatCast(inst_ty, pt), inst_ty, zcu)) {
  28682                         return sema.fail(
  28683                             block,
  28684                             inst_src,
  28685                             "type '{f}' cannot represent float value '{f}'",
  28686                             .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) },
  28687                         );
  28688                     }
  28689                     return Air.internedToRef(result_val.toIntern());
  28690                 } else if (dest_ty.zigTypeTag(zcu) == .comptime_float) {
  28691                     if (!opts.report_err) return error.NotCoercible;
  28692                     return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float });
  28693                 }
  28694 
  28695                 // float widening
  28696                 const src_bits = inst_ty.floatBits(target);
  28697                 const dst_bits = dest_ty.floatBits(target);
  28698                 if (dst_bits >= src_bits) {
  28699                     try sema.requireRuntimeBlock(block, inst_src, null);
  28700                     return block.addTyOp(.fpext, dest_ty, inst);
  28701                 }
  28702             },
  28703             .int, .comptime_int => int: {
  28704                 const val = maybe_inst_val orelse {
  28705                     if (dest_ty.zigTypeTag(zcu) == .comptime_float) {
  28706                         if (!opts.report_err) return error.NotCoercible;
  28707                         return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float });
  28708                     }
  28709                     break :int;
  28710                 };
  28711                 const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema);
  28712                 const fits: bool = switch (ip.indexToKey(result_val.toIntern())) {
  28713                     else => unreachable,
  28714                     .undef => true,
  28715                     .float => |float| fits: {
  28716                         var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  28717                         const operand_big_int = val.toBigInt(&buffer, zcu);
  28718                         switch (float.storage) {
  28719                             inline else => |x| {
  28720                                 if (!std.math.isFinite(x)) break :fits false;
  28721                                 var result_big_int: std.math.big.int.Mutable = .{
  28722                                     .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)),
  28723                                     .len = undefined,
  28724                                     .positive = undefined,
  28725                                 };
  28726                                 switch (result_big_int.setFloat(x, .nearest_even)) {
  28727                                     .inexact => break :fits false,
  28728                                     .exact => {},
  28729                                 }
  28730                                 break :fits result_big_int.toConst().eql(operand_big_int);
  28731                             },
  28732                         }
  28733                     },
  28734                 };
  28735                 if (!fits) return sema.fail(
  28736                     block,
  28737                     inst_src,
  28738                     "type '{f}' cannot represent integer value '{f}'",
  28739                     .{ dest_ty.fmt(pt), val.fmtValue(pt) },
  28740                 );
  28741                 return .fromValue(result_val);
  28742             },
  28743             else => {},
  28744         },
  28745         .@"enum" => switch (inst_ty.zigTypeTag(zcu)) {
  28746             .enum_literal => {
  28747                 // enum literal to enum
  28748                 const val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
  28749                 const string = zcu.intern_pool.indexToKey(val.toIntern()).enum_literal;
  28750                 const field_index = dest_ty.enumFieldIndex(string, zcu) orelse {
  28751                     return sema.fail(block, inst_src, "no field named '{f}' in enum '{f}'", .{
  28752                         string.fmt(&zcu.intern_pool), dest_ty.fmt(pt),
  28753                     });
  28754                 };
  28755                 return Air.internedToRef((try pt.enumValueFieldIndex(dest_ty, @intCast(field_index))).toIntern());
  28756             },
  28757             .@"union" => blk: {
  28758                 // union to its own tag type
  28759                 const union_tag_ty = inst_ty.unionTagType(zcu) orelse break :blk;
  28760                 if (union_tag_ty.eql(dest_ty, zcu)) {
  28761                     return sema.unionToTag(block, dest_ty, inst, inst_src);
  28762                 }
  28763             },
  28764             else => {},
  28765         },
  28766         .error_union => switch (inst_ty.zigTypeTag(zcu)) {
  28767             .error_union => eu: {
  28768                 if (maybe_inst_val) |inst_val| {
  28769                     switch (inst_val.toIntern()) {
  28770                         .undef => return pt.undefRef(dest_ty),
  28771                         else => switch (zcu.intern_pool.indexToKey(inst_val.toIntern())) {
  28772                             .error_union => |error_union| switch (error_union.val) {
  28773                                 .err_name => |err_name| {
  28774                                     const error_set_ty = inst_ty.errorUnionSet(zcu);
  28775                                     const error_set_val = Air.internedToRef((try pt.intern(.{ .err = .{
  28776                                         .ty = error_set_ty.toIntern(),
  28777                                         .name = err_name,
  28778                                     } })));
  28779                                     return sema.wrapErrorUnionSet(block, dest_ty, error_set_val, inst_src);
  28780                                 },
  28781                                 .payload => |payload| {
  28782                                     const payload_val = Air.internedToRef(payload);
  28783                                     return sema.wrapErrorUnionPayload(block, dest_ty, payload_val, inst_src) catch |err| switch (err) {
  28784                                         error.NotCoercible => break :eu,
  28785                                         else => |e| return e,
  28786                                     };
  28787                                 },
  28788                             },
  28789                             else => unreachable,
  28790                         },
  28791                     }
  28792                 }
  28793             },
  28794             .error_set => {
  28795                 // E to E!T
  28796                 return sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src);
  28797             },
  28798             else => eu: {
  28799                 // T to E!T
  28800                 return sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  28801                     error.NotCoercible => {
  28802                         if (in_memory_result == .no_match) {
  28803                             const payload_type = dest_ty.errorUnionPayload(zcu);
  28804                             // Try to give more useful notes
  28805                             in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
  28806                         }
  28807                         break :eu;
  28808                     },
  28809                     else => |e| return e,
  28810                 };
  28811             },
  28812         },
  28813         .@"union" => switch (inst_ty.zigTypeTag(zcu)) {
  28814             .@"enum", .enum_literal => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
  28815             else => {},
  28816         },
  28817         .array => switch (inst_ty.zigTypeTag(zcu)) {
  28818             .array => array_to_array: {
  28819                 // Array coercions are allowed only if the child is IMC and the sentinel is unchanged or removed.
  28820                 if (.ok != try sema.coerceInMemoryAllowed(
  28821                     block,
  28822                     dest_ty.childType(zcu),
  28823                     inst_ty.childType(zcu),
  28824                     false,
  28825                     target,
  28826                     dest_ty_src,
  28827                     inst_src,
  28828                     maybe_inst_val,
  28829                 )) {
  28830                     break :array_to_array;
  28831                 }
  28832 
  28833                 if (dest_ty.sentinel(zcu)) |dest_sent| {
  28834                     const src_sent = inst_ty.sentinel(zcu) orelse break :array_to_array;
  28835                     if (dest_sent.toIntern() != (try pt.getCoerced(src_sent, dest_ty.childType(zcu))).toIntern()) {
  28836                         break :array_to_array;
  28837                     }
  28838                 }
  28839 
  28840                 return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src);
  28841             },
  28842             .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  28843             .@"struct" => {
  28844                 if (inst_ty.isTuple(zcu)) {
  28845                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  28846                 }
  28847             },
  28848             else => {},
  28849         },
  28850         .vector => switch (inst_ty.zigTypeTag(zcu)) {
  28851             .array, .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
  28852             .@"struct" => {
  28853                 if (inst_ty.isTuple(zcu)) {
  28854                     return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
  28855                 }
  28856             },
  28857             else => {},
  28858         },
  28859         .@"struct" => blk: {
  28860             if (dest_ty.isTuple(zcu) and inst_ty.isTuple(zcu)) {
  28861                 return sema.coerceTupleToTuple(block, dest_ty, inst, inst_src) catch |err| switch (err) {
  28862                     error.NotCoercible => break :blk,
  28863                     else => |e| return e,
  28864                 };
  28865             }
  28866         },
  28867         else => {},
  28868     }
  28869 
  28870     const can_coerce_to = switch (dest_ty.zigTypeTag(zcu)) {
  28871         .noreturn, .@"opaque" => false,
  28872         else => true,
  28873     };
  28874 
  28875     if (can_coerce_to) {
  28876         // undefined to anything. We do this after the big switch above so that
  28877         // special logic has a chance to run first, such as `*[N]T` to `[]T` which
  28878         // should initialize the length field of the slice.
  28879         if (maybe_inst_val) |val| if (val.toIntern() == .undef) return pt.undefRef(dest_ty);
  28880     }
  28881 
  28882     if (!opts.report_err) return error.NotCoercible;
  28883 
  28884     if (opts.is_ret and dest_ty.zigTypeTag(zcu) == .noreturn) {
  28885         const msg = msg: {
  28886             const msg = try sema.errMsg(inst_src, "function declared 'noreturn' returns", .{});
  28887             errdefer msg.destroy(sema.gpa);
  28888 
  28889             const ret_ty_src: LazySrcLoc = .{
  28890                 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip),
  28891                 .offset = .{ .node_offset_fn_type_ret_ty = .zero },
  28892             };
  28893             try sema.errNote(ret_ty_src, msg, "'noreturn' declared here", .{});
  28894             break :msg msg;
  28895         };
  28896         return sema.failWithOwnedErrorMsg(block, msg);
  28897     }
  28898 
  28899     const msg = msg: {
  28900         const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{ dest_ty.fmt(pt), inst_ty.fmt(pt) });
  28901         errdefer msg.destroy(sema.gpa);
  28902 
  28903         if (!can_coerce_to) {
  28904             try sema.errNote(inst_src, msg, "cannot coerce to '{f}'", .{dest_ty.fmt(pt)});
  28905         }
  28906 
  28907         // E!T to T
  28908         if (inst_ty.zigTypeTag(zcu) == .error_union and
  28909             (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok)
  28910         {
  28911             try sema.errNote(inst_src, msg, "cannot convert error union to payload type", .{});
  28912             try sema.errNote(inst_src, msg, "consider using 'try', 'catch', or 'if'", .{});
  28913         }
  28914 
  28915         // ?T to T
  28916         if (inst_ty.zigTypeTag(zcu) == .optional and
  28917             (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(zcu), dest_ty, false, target, dest_ty_src, inst_src, maybe_inst_val)) == .ok)
  28918         {
  28919             try sema.errNote(inst_src, msg, "cannot convert optional to payload type", .{});
  28920             try sema.errNote(inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
  28921         }
  28922 
  28923         try in_memory_result.report(sema, inst_src, msg);
  28924 
  28925         // Add notes about function return type
  28926         if (opts.is_ret and
  28927             !zcu.test_functions.contains(zcu.funcInfo(sema.func_index).owner_nav))
  28928         {
  28929             const ret_ty_src: LazySrcLoc = .{
  28930                 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip),
  28931                 .offset = .{ .node_offset_fn_type_ret_ty = .zero },
  28932             };
  28933             if (inst_ty.isError(zcu) and !dest_ty.isError(zcu)) {
  28934                 try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{});
  28935             } else {
  28936                 try sema.errNote(ret_ty_src, msg, "function return type declared here", .{});
  28937             }
  28938         }
  28939 
  28940         if (try opts.param_src.get(sema)) |param_src| {
  28941             try sema.errNote(param_src, msg, "parameter type declared here", .{});
  28942         }
  28943 
  28944         // TODO maybe add "cannot store an error in type '{f}'" note
  28945 
  28946         break :msg msg;
  28947     };
  28948     return sema.failWithOwnedErrorMsg(block, msg);
  28949 }
  28950 
  28951 fn coerceInMemory(
  28952     sema: *Sema,
  28953     val: Value,
  28954     dst_ty: Type,
  28955 ) CompileError!Air.Inst.Ref {
  28956     return Air.internedToRef((try sema.pt.getCoerced(val, dst_ty)).toIntern());
  28957 }
  28958 
  28959 const InMemoryCoercionResult = union(enum) {
  28960     ok,
  28961     no_match: Pair,
  28962     int_not_coercible: Int,
  28963     comptime_int_not_coercible: TypeValuePair,
  28964     error_union_payload: PairAndChild,
  28965     array_len: IntPair,
  28966     array_sentinel: Sentinel,
  28967     array_elem: PairAndChild,
  28968     vector_len: IntPair,
  28969     vector_elem: PairAndChild,
  28970     optional_shape: Pair,
  28971     optional_child: PairAndChild,
  28972     from_anyerror,
  28973     missing_error: []const InternPool.NullTerminatedString,
  28974     /// true if wanted is var args
  28975     fn_var_args: bool,
  28976     /// true if wanted is generic
  28977     fn_generic: bool,
  28978     fn_param_count: IntPair,
  28979     fn_param_noalias: IntPair,
  28980     fn_param_comptime: ComptimeParam,
  28981     fn_param: Param,
  28982     fn_cc: CC,
  28983     fn_return_type: PairAndChild,
  28984     ptr_child: PairAndChild,
  28985     ptr_addrspace: AddressSpace,
  28986     ptr_sentinel: Sentinel,
  28987     ptr_size: Size,
  28988     ptr_const: Pair,
  28989     ptr_volatile: Pair,
  28990     ptr_allowzero: Pair,
  28991     ptr_bit_range: BitRange,
  28992     ptr_alignment: AlignPair,
  28993     double_ptr_to_anyopaque: Pair,
  28994     slice_to_anyopaque: Pair,
  28995 
  28996     const Pair = struct {
  28997         actual: Type,
  28998         wanted: Type,
  28999     };
  29000 
  29001     const TypeValuePair = struct {
  29002         actual: Value,
  29003         wanted: Type,
  29004     };
  29005 
  29006     const PairAndChild = struct {
  29007         child: *InMemoryCoercionResult,
  29008         actual: Type,
  29009         wanted: Type,
  29010     };
  29011 
  29012     const Param = struct {
  29013         child: *InMemoryCoercionResult,
  29014         actual: Type,
  29015         wanted: Type,
  29016         index: u64,
  29017     };
  29018 
  29019     const ComptimeParam = struct {
  29020         index: u64,
  29021         wanted: bool,
  29022     };
  29023 
  29024     const Sentinel = struct {
  29025         // unreachable_value indicates no sentinel
  29026         actual: Value,
  29027         wanted: Value,
  29028         ty: Type,
  29029     };
  29030 
  29031     const Int = struct {
  29032         actual_signedness: std.builtin.Signedness,
  29033         wanted_signedness: std.builtin.Signedness,
  29034         actual_bits: u16,
  29035         wanted_bits: u16,
  29036     };
  29037 
  29038     const IntPair = struct {
  29039         actual: u64,
  29040         wanted: u64,
  29041     };
  29042 
  29043     const AlignPair = struct {
  29044         actual: Alignment,
  29045         wanted: Alignment,
  29046     };
  29047 
  29048     const Size = struct {
  29049         actual: std.builtin.Type.Pointer.Size,
  29050         wanted: std.builtin.Type.Pointer.Size,
  29051     };
  29052 
  29053     const AddressSpace = struct {
  29054         actual: std.builtin.AddressSpace,
  29055         wanted: std.builtin.AddressSpace,
  29056     };
  29057 
  29058     const CC = struct {
  29059         actual: std.builtin.CallingConvention,
  29060         wanted: std.builtin.CallingConvention,
  29061     };
  29062 
  29063     const BitRange = struct {
  29064         actual_host: u16,
  29065         wanted_host: u16,
  29066         actual_offset: u16,
  29067         wanted_offset: u16,
  29068     };
  29069 
  29070     fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult {
  29071         const res = try arena.create(InMemoryCoercionResult);
  29072         res.* = child.*;
  29073         return res;
  29074     }
  29075 
  29076     fn report(res: *const InMemoryCoercionResult, sema: *Sema, src: LazySrcLoc, msg: *Zcu.ErrorMsg) !void {
  29077         const pt = sema.pt;
  29078         var cur = res;
  29079         while (true) switch (cur.*) {
  29080             .ok => unreachable,
  29081             .no_match => |types| {
  29082                 try sema.addDeclaredHereNote(msg, types.wanted);
  29083                 try sema.addDeclaredHereNote(msg, types.actual);
  29084                 break;
  29085             },
  29086             .int_not_coercible => |int| {
  29087                 try sema.errNote(src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{
  29088                     @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits,
  29089                 });
  29090                 break;
  29091             },
  29092             .comptime_int_not_coercible => |int| {
  29093                 try sema.errNote(src, msg, "type '{f}' cannot represent value '{f}'", .{
  29094                     int.wanted.fmt(pt), int.actual.fmtValueSema(pt, sema),
  29095                 });
  29096                 break;
  29097             },
  29098             .error_union_payload => |pair| {
  29099                 try sema.errNote(src, msg, "error union payload '{f}' cannot cast into error union payload '{f}'", .{
  29100                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29101                 });
  29102                 cur = pair.child;
  29103             },
  29104             .array_len => |lens| {
  29105                 try sema.errNote(src, msg, "array of length {d} cannot cast into an array of length {d}", .{
  29106                     lens.actual, lens.wanted,
  29107                 });
  29108                 break;
  29109             },
  29110             .array_sentinel => |sentinel| {
  29111                 if (sentinel.actual.toIntern() != .unreachable_value) {
  29112                     try sema.errNote(src, msg, "array sentinel '{f}' cannot cast into array sentinel '{f}'", .{
  29113                         sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema),
  29114                     });
  29115                 } else {
  29116                     try sema.errNote(src, msg, "destination array requires '{f}' sentinel", .{
  29117                         sentinel.wanted.fmtValueSema(pt, sema),
  29118                     });
  29119                 }
  29120                 break;
  29121             },
  29122             .array_elem => |pair| {
  29123                 try sema.errNote(src, msg, "array element type '{f}' cannot cast into array element type '{f}'", .{
  29124                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29125                 });
  29126                 cur = pair.child;
  29127             },
  29128             .vector_len => |lens| {
  29129                 try sema.errNote(src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{
  29130                     lens.actual, lens.wanted,
  29131                 });
  29132                 break;
  29133             },
  29134             .vector_elem => |pair| {
  29135                 try sema.errNote(src, msg, "vector element type '{f}' cannot cast into vector element type '{f}'", .{
  29136                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29137                 });
  29138                 cur = pair.child;
  29139             },
  29140             .optional_shape => |pair| {
  29141                 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{
  29142                     pair.actual.optionalChild(pt.zcu).fmt(pt), pair.wanted.optionalChild(pt.zcu).fmt(pt),
  29143                 });
  29144                 break;
  29145             },
  29146             .optional_child => |pair| {
  29147                 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{
  29148                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29149                 });
  29150                 cur = pair.child;
  29151             },
  29152             .from_anyerror => {
  29153                 try sema.errNote(src, msg, "global error set cannot cast into a smaller set", .{});
  29154                 break;
  29155             },
  29156             .missing_error => |missing_errors| {
  29157                 for (missing_errors) |err| {
  29158                     try sema.errNote(src, msg, "'error.{f}' not a member of destination error set", .{err.fmt(&pt.zcu.intern_pool)});
  29159                 }
  29160                 break;
  29161             },
  29162             .fn_var_args => |wanted_var_args| {
  29163                 if (wanted_var_args) {
  29164                     try sema.errNote(src, msg, "non-variadic function cannot cast into a variadic function", .{});
  29165                 } else {
  29166                     try sema.errNote(src, msg, "variadic function cannot cast into a non-variadic function", .{});
  29167                 }
  29168                 break;
  29169             },
  29170             .fn_generic => |wanted_generic| {
  29171                 if (wanted_generic) {
  29172                     try sema.errNote(src, msg, "non-generic function cannot cast into a generic function", .{});
  29173                 } else {
  29174                     try sema.errNote(src, msg, "generic function cannot cast into a non-generic function", .{});
  29175                 }
  29176                 break;
  29177             },
  29178             .fn_param_count => |lens| {
  29179                 try sema.errNote(src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{
  29180                     lens.actual, lens.wanted,
  29181                 });
  29182                 break;
  29183             },
  29184             .fn_param_noalias => |param| {
  29185                 var index: u6 = 0;
  29186                 var actual_noalias = false;
  29187                 while (true) : (index += 1) {
  29188                     const actual: u1 = @truncate(param.actual >> index);
  29189                     const wanted: u1 = @truncate(param.wanted >> index);
  29190                     if (actual != wanted) {
  29191                         actual_noalias = actual == 1;
  29192                         break;
  29193                     }
  29194                 }
  29195                 if (!actual_noalias) {
  29196                     try sema.errNote(src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index});
  29197                 } else {
  29198                     try sema.errNote(src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index});
  29199                 }
  29200                 break;
  29201             },
  29202             .fn_param_comptime => |param| {
  29203                 if (param.wanted) {
  29204                     try sema.errNote(src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index});
  29205                 } else {
  29206                     try sema.errNote(src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index});
  29207                 }
  29208                 break;
  29209             },
  29210             .fn_param => |param| {
  29211                 try sema.errNote(src, msg, "parameter {d} '{f}' cannot cast into '{f}'", .{
  29212                     param.index, param.actual.fmt(pt), param.wanted.fmt(pt),
  29213                 });
  29214                 cur = param.child;
  29215             },
  29216             .fn_cc => |cc| {
  29217                 try sema.errNote(src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) });
  29218                 break;
  29219             },
  29220             .fn_return_type => |pair| {
  29221                 try sema.errNote(src, msg, "return type '{f}' cannot cast into return type '{f}'", .{
  29222                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29223                 });
  29224                 cur = pair.child;
  29225             },
  29226             .ptr_child => |pair| {
  29227                 try sema.errNote(src, msg, "pointer type child '{f}' cannot cast into pointer type child '{f}'", .{
  29228                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29229                 });
  29230                 cur = pair.child;
  29231             },
  29232             .ptr_addrspace => |@"addrspace"| {
  29233                 try sema.errNote(src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) });
  29234                 break;
  29235             },
  29236             .ptr_sentinel => |sentinel| {
  29237                 if (sentinel.actual.toIntern() != .unreachable_value) {
  29238                     try sema.errNote(src, msg, "pointer sentinel '{f}' cannot cast into pointer sentinel '{f}'", .{
  29239                         sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema),
  29240                     });
  29241                 } else {
  29242                     try sema.errNote(src, msg, "destination pointer requires '{f}' sentinel", .{
  29243                         sentinel.wanted.fmtValueSema(pt, sema),
  29244                     });
  29245                 }
  29246                 break;
  29247             },
  29248             .ptr_size => |size| {
  29249                 try sema.errNote(src, msg, "a {s} cannot cast into a {s}", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) });
  29250                 break;
  29251             },
  29252             .ptr_allowzero => |pair| {
  29253                 const wanted_allow_zero = pair.wanted.ptrAllowsZero(pt.zcu);
  29254                 const actual_allow_zero = pair.actual.ptrAllowsZero(pt.zcu);
  29255                 if (actual_allow_zero and !wanted_allow_zero) {
  29256                     try sema.errNote(src, msg, "'{f}' could have null values which are illegal in type '{f}'", .{
  29257                         pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29258                     });
  29259                 } else {
  29260                     try sema.errNote(src, msg, "mutable '{f}' would allow illegal null values stored to type '{f}'", .{
  29261                         pair.wanted.fmt(pt), pair.actual.fmt(pt),
  29262                     });
  29263                 }
  29264                 break;
  29265             },
  29266             .ptr_const => |pair| {
  29267                 const wanted_const = pair.wanted.isConstPtr(pt.zcu);
  29268                 const actual_const = pair.actual.isConstPtr(pt.zcu);
  29269                 if (actual_const and !wanted_const) {
  29270                     try sema.errNote(src, msg, "cast discards const qualifier", .{});
  29271                 } else {
  29272                     try sema.errNote(src, msg, "mutable '{f}' would allow illegal const pointers stored to type '{f}'", .{
  29273                         pair.wanted.fmt(pt), pair.actual.fmt(pt),
  29274                     });
  29275                 }
  29276                 break;
  29277             },
  29278             .ptr_volatile => |pair| {
  29279                 const wanted_volatile = pair.wanted.isVolatilePtr(pt.zcu);
  29280                 const actual_volatile = pair.actual.isVolatilePtr(pt.zcu);
  29281                 if (actual_volatile and !wanted_volatile) {
  29282                     try sema.errNote(src, msg, "cast discards volatile qualifier", .{});
  29283                 } else {
  29284                     try sema.errNote(src, msg, "mutable '{f}' would allow illegal volatile pointers stored to type '{f}'", .{
  29285                         pair.wanted.fmt(pt), pair.actual.fmt(pt),
  29286                     });
  29287                 }
  29288                 break;
  29289             },
  29290             .ptr_bit_range => |bit_range| {
  29291                 if (bit_range.actual_host != bit_range.wanted_host) {
  29292                     try sema.errNote(src, msg, "pointer host size '{d}' cannot cast into pointer host size '{d}'", .{
  29293                         bit_range.actual_host, bit_range.wanted_host,
  29294                     });
  29295                 }
  29296                 if (bit_range.actual_offset != bit_range.wanted_offset) {
  29297                     try sema.errNote(src, msg, "pointer bit offset '{d}' cannot cast into pointer bit offset '{d}'", .{
  29298                         bit_range.actual_offset, bit_range.wanted_offset,
  29299                     });
  29300                 }
  29301                 break;
  29302             },
  29303             .ptr_alignment => |pair| {
  29304                 try sema.errNote(src, msg, "pointer alignment '{d}' cannot cast into pointer alignment '{d}'", .{
  29305                     pair.actual.toByteUnits() orelse 0, pair.wanted.toByteUnits() orelse 0,
  29306                 });
  29307                 break;
  29308             },
  29309             .double_ptr_to_anyopaque => |pair| {
  29310                 try sema.errNote(src, msg, "cannot implicitly cast double pointer '{f}' to anyopaque pointer '{f}'", .{
  29311                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29312                 });
  29313                 break;
  29314             },
  29315             .slice_to_anyopaque => |pair| {
  29316                 try sema.errNote(src, msg, "cannot implicitly cast slice '{f}' to anyopaque pointer '{f}'", .{
  29317                     pair.actual.fmt(pt), pair.wanted.fmt(pt),
  29318                 });
  29319                 try sema.errNote(src, msg, "consider using '.ptr'", .{});
  29320                 break;
  29321             },
  29322         };
  29323     }
  29324 };
  29325 
  29326 fn pointerSizeString(size: std.builtin.Type.Pointer.Size) []const u8 {
  29327     return switch (size) {
  29328         .one => "single pointer",
  29329         .many => "many pointer",
  29330         .c => "C pointer",
  29331         .slice => "slice",
  29332     };
  29333 }
  29334 
  29335 /// If types `A` and `B` have identical representations in runtime memory, they are considered
  29336 /// "in-memory coercible". This is a subset of normal coercions. Not only can `A` coerce to `B`, but
  29337 /// also, coercions can happen through pointers. For instance, `*const A` can coerce to `*const B`.
  29338 ///
  29339 /// If this function is called, the coercion must be applied, or a compile error emitted if `.ok`
  29340 /// is not returned. This is because this function may modify inferred error sets to make a
  29341 /// coercion possible, even if `.ok` is not returned.
  29342 pub fn coerceInMemoryAllowed(
  29343     sema: *Sema,
  29344     block: *Block,
  29345     dest_ty: Type,
  29346     src_ty: Type,
  29347     /// If `true`, this query comes from an attempted coercion of the form `*Src` -> `*Dest`, where
  29348     /// both pointers are mutable. If this coercion is allowed, one could store to the `*Dest` and
  29349     /// load from the `*Src` to effectively perform an in-memory coercion from `Dest` to `Src`.
  29350     /// Therefore, when `dest_is_mut`, the in-memory coercion must be valid in *both directions*.
  29351     dest_is_mut: bool,
  29352     target: *const std.Target,
  29353     dest_src: LazySrcLoc,
  29354     src_src: LazySrcLoc,
  29355     src_val: ?Value,
  29356 ) CompileError!InMemoryCoercionResult {
  29357     const pt = sema.pt;
  29358     const zcu = pt.zcu;
  29359 
  29360     if (dest_ty.eql(src_ty, zcu))
  29361         return .ok;
  29362 
  29363     const dest_tag = dest_ty.zigTypeTag(zcu);
  29364     const src_tag = src_ty.zigTypeTag(zcu);
  29365 
  29366     // Differently-named integers with the same number of bits.
  29367     if (dest_tag == .int and src_tag == .int) {
  29368         const dest_info = dest_ty.intInfo(zcu);
  29369         const src_info = src_ty.intInfo(zcu);
  29370 
  29371         if (dest_info.signedness == src_info.signedness and
  29372             dest_info.bits == src_info.bits)
  29373         {
  29374             return .ok;
  29375         }
  29376 
  29377         if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or
  29378             // small enough unsigned ints can get casted to large enough signed ints
  29379             (dest_info.signedness == .signed and src_info.signedness == .unsigned and dest_info.bits <= src_info.bits) or
  29380             (dest_info.signedness == .unsigned and src_info.signedness == .signed))
  29381         {
  29382             return InMemoryCoercionResult{ .int_not_coercible = .{
  29383                 .actual_signedness = src_info.signedness,
  29384                 .wanted_signedness = dest_info.signedness,
  29385                 .actual_bits = src_info.bits,
  29386                 .wanted_bits = dest_info.bits,
  29387             } };
  29388         }
  29389     }
  29390 
  29391     // Comptime int to regular int.
  29392     if (dest_tag == .int and src_tag == .comptime_int) {
  29393         if (src_val) |val| {
  29394             if (!(try sema.intFitsInType(val, dest_ty, null))) {
  29395                 return .{ .comptime_int_not_coercible = .{ .wanted = dest_ty, .actual = val } };
  29396             }
  29397         }
  29398     }
  29399 
  29400     // Differently-named floats with the same number of bits.
  29401     if (dest_tag == .float and src_tag == .float) {
  29402         const dest_bits = dest_ty.floatBits(target);
  29403         const src_bits = src_ty.floatBits(target);
  29404         if (dest_bits == src_bits) {
  29405             return .ok;
  29406         }
  29407     }
  29408 
  29409     // Pointers / Pointer-like Optionals
  29410     const maybe_dest_ptr_ty = try sema.typePtrOrOptionalPtrTy(dest_ty);
  29411     const maybe_src_ptr_ty = try sema.typePtrOrOptionalPtrTy(src_ty);
  29412     if (maybe_dest_ptr_ty) |dest_ptr_ty| {
  29413         if (maybe_src_ptr_ty) |src_ptr_ty| {
  29414             return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ptr_ty, src_ptr_ty, dest_is_mut, target, dest_src, src_src);
  29415         }
  29416     }
  29417 
  29418     // Slices
  29419     if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) {
  29420         return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
  29421     }
  29422 
  29423     // Functions
  29424     if (dest_tag == .@"fn" and src_tag == .@"fn") {
  29425         return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
  29426     }
  29427 
  29428     // Error Unions
  29429     if (dest_tag == .error_union and src_tag == .error_union) {
  29430         const dest_payload = dest_ty.errorUnionPayload(zcu);
  29431         const src_payload = src_ty.errorUnionPayload(zcu);
  29432         const child = try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src, null);
  29433         if (child != .ok) {
  29434             return .{ .error_union_payload = .{
  29435                 .child = try child.dupe(sema.arena),
  29436                 .actual = src_payload,
  29437                 .wanted = dest_payload,
  29438             } };
  29439         }
  29440         return try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(zcu), src_ty.errorUnionSet(zcu), dest_is_mut, target, dest_src, src_src, null);
  29441     }
  29442 
  29443     // Error Sets
  29444     if (dest_tag == .error_set and src_tag == .error_set) {
  29445         const res1 = try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src);
  29446         if (!dest_is_mut or res1 != .ok) return res1;
  29447         // src -> dest is okay, but `dest_is_mut`, so it needs to be allowed in the other direction.
  29448         const res2 = try sema.coerceInMemoryAllowedErrorSets(block, src_ty, dest_ty, src_src, dest_src);
  29449         return res2;
  29450     }
  29451 
  29452     // Arrays
  29453     if (dest_tag == .array and src_tag == .array) {
  29454         const dest_info = dest_ty.arrayInfo(zcu);
  29455         const src_info = src_ty.arrayInfo(zcu);
  29456         if (dest_info.len != src_info.len) {
  29457             return .{ .array_len = .{
  29458                 .actual = src_info.len,
  29459                 .wanted = dest_info.len,
  29460             } };
  29461         }
  29462 
  29463         const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src, null);
  29464         switch (child) {
  29465             .ok => {},
  29466             .no_match => return child,
  29467             else => {
  29468                 return .{ .array_elem = .{
  29469                     .child = try child.dupe(sema.arena),
  29470                     .actual = src_info.elem_type,
  29471                     .wanted = dest_info.elem_type,
  29472                 } };
  29473             },
  29474         }
  29475         const ok_sent = (dest_info.sentinel == null and src_info.sentinel == null) or
  29476             (src_info.sentinel != null and
  29477                 dest_info.sentinel != null and
  29478                 dest_info.sentinel.?.eql(
  29479                     try pt.getCoerced(src_info.sentinel.?, dest_info.elem_type),
  29480                     dest_info.elem_type,
  29481                     zcu,
  29482                 ));
  29483         if (!ok_sent) {
  29484             return .{ .array_sentinel = .{
  29485                 .actual = src_info.sentinel orelse Value.@"unreachable",
  29486                 .wanted = dest_info.sentinel orelse Value.@"unreachable",
  29487                 .ty = dest_info.elem_type,
  29488             } };
  29489         }
  29490         return .ok;
  29491     }
  29492 
  29493     // Vectors
  29494     if (dest_tag == .vector and src_tag == .vector) {
  29495         const dest_len = dest_ty.vectorLen(zcu);
  29496         const src_len = src_ty.vectorLen(zcu);
  29497         if (dest_len != src_len) {
  29498             return .{ .vector_len = .{
  29499                 .actual = src_len,
  29500                 .wanted = dest_len,
  29501             } };
  29502         }
  29503 
  29504         const dest_elem_ty = dest_ty.scalarType(zcu);
  29505         const src_elem_ty = src_ty.scalarType(zcu);
  29506         const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null);
  29507         if (child != .ok) {
  29508             return .{ .vector_elem = .{
  29509                 .child = try child.dupe(sema.arena),
  29510                 .actual = src_elem_ty,
  29511                 .wanted = dest_elem_ty,
  29512             } };
  29513         }
  29514 
  29515         return .ok;
  29516     }
  29517 
  29518     // Arrays <-> Vectors
  29519     if ((dest_tag == .vector and src_tag == .array) or
  29520         (dest_tag == .array and src_tag == .vector))
  29521     {
  29522         const dest_len = dest_ty.arrayLen(zcu);
  29523         const src_len = src_ty.arrayLen(zcu);
  29524         if (dest_len != src_len) {
  29525             return .{ .array_len = .{
  29526                 .actual = src_len,
  29527                 .wanted = dest_len,
  29528             } };
  29529         }
  29530 
  29531         const dest_elem_ty = dest_ty.childType(zcu);
  29532         const src_elem_ty = src_ty.childType(zcu);
  29533         const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null);
  29534         if (child != .ok) {
  29535             return .{ .array_elem = .{
  29536                 .child = try child.dupe(sema.arena),
  29537                 .actual = src_elem_ty,
  29538                 .wanted = dest_elem_ty,
  29539             } };
  29540         }
  29541 
  29542         if (dest_tag == .array) {
  29543             const dest_info = dest_ty.arrayInfo(zcu);
  29544             if (dest_info.sentinel != null) {
  29545                 return .{ .array_sentinel = .{
  29546                     .actual = Value.@"unreachable",
  29547                     .wanted = dest_info.sentinel.?,
  29548                     .ty = dest_info.elem_type,
  29549                 } };
  29550             }
  29551         }
  29552 
  29553         // The memory layout of @Vector(N, iM) is the same as the integer type i(N*M),
  29554         // that is to say, the padding bits are not in the same place as the array [N]iM.
  29555         // If there's no padding, the bitcast is possible.
  29556         const elem_bit_size = dest_elem_ty.bitSize(zcu);
  29557         const elem_abi_byte_size = dest_elem_ty.abiSize(zcu);
  29558         if (elem_abi_byte_size * 8 == elem_bit_size)
  29559             return .ok;
  29560     }
  29561 
  29562     // Optionals
  29563     if (dest_tag == .optional and src_tag == .optional) {
  29564         if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
  29565             return .{ .optional_shape = .{
  29566                 .actual = src_ty,
  29567                 .wanted = dest_ty,
  29568             } };
  29569         }
  29570         const dest_child_type = dest_ty.optionalChild(zcu);
  29571         const src_child_type = src_ty.optionalChild(zcu);
  29572 
  29573         const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src, null);
  29574         if (child != .ok) {
  29575             return .{ .optional_child = .{
  29576                 .child = try child.dupe(sema.arena),
  29577                 .actual = src_child_type,
  29578                 .wanted = dest_child_type,
  29579             } };
  29580         }
  29581 
  29582         return .ok;
  29583     }
  29584 
  29585     // Tuples (with in-memory-coercible fields)
  29586     if (dest_ty.isTuple(zcu) and src_ty.isTuple(zcu)) tuple: {
  29587         if (dest_ty.structFieldCount(zcu) != src_ty.structFieldCount(zcu)) break :tuple;
  29588         const field_count = dest_ty.structFieldCount(zcu);
  29589         for (0..field_count) |field_idx| {
  29590             if (dest_ty.structFieldIsComptime(field_idx, zcu) != src_ty.structFieldIsComptime(field_idx, zcu)) break :tuple;
  29591             if (dest_ty.fieldAlignment(field_idx, zcu) != src_ty.fieldAlignment(field_idx, zcu)) break :tuple;
  29592             const dest_field_ty = dest_ty.fieldType(field_idx, zcu);
  29593             const src_field_ty = src_ty.fieldType(field_idx, zcu);
  29594             const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src, null);
  29595             if (field != .ok) break :tuple;
  29596         }
  29597         return .ok;
  29598     }
  29599 
  29600     return .{ .no_match = .{
  29601         .actual = dest_ty,
  29602         .wanted = src_ty,
  29603     } };
  29604 }
  29605 
  29606 fn coerceInMemoryAllowedErrorSets(
  29607     sema: *Sema,
  29608     block: *Block,
  29609     dest_ty: Type,
  29610     src_ty: Type,
  29611     dest_src: LazySrcLoc,
  29612     src_src: LazySrcLoc,
  29613 ) !InMemoryCoercionResult {
  29614     const pt = sema.pt;
  29615     const zcu = pt.zcu;
  29616     const gpa = sema.gpa;
  29617     const ip = &zcu.intern_pool;
  29618 
  29619     // Coercion to `anyerror`. Note that this check can return false negatives
  29620     // in case the error sets did not get resolved.
  29621     if (dest_ty.isAnyError(zcu)) {
  29622         return .ok;
  29623     }
  29624 
  29625     if (dest_ty.toIntern() == .adhoc_inferred_error_set_type) {
  29626         // We are trying to coerce an error set to the current function's
  29627         // inferred error set.
  29628         const dst_ies = sema.fn_ret_ty_ies.?;
  29629         try dst_ies.addErrorSet(src_ty, ip, sema.arena);
  29630         return .ok;
  29631     }
  29632 
  29633     if (ip.isInferredErrorSetType(dest_ty.toIntern())) {
  29634         const dst_ies_func_index = ip.iesFuncIndex(dest_ty.toIntern());
  29635         if (sema.fn_ret_ty_ies) |dst_ies| {
  29636             if (dst_ies.func == dst_ies_func_index) {
  29637                 // We are trying to coerce an error set to the current function's
  29638                 // inferred error set.
  29639                 try dst_ies.addErrorSet(src_ty, ip, sema.arena);
  29640                 return .ok;
  29641             }
  29642         }
  29643         switch (try sema.resolveInferredErrorSet(block, dest_src, dest_ty.toIntern())) {
  29644             // isAnyError might have changed from a false negative to a true
  29645             // positive after resolution.
  29646             .anyerror_type => return .ok,
  29647             else => {},
  29648         }
  29649     }
  29650 
  29651     var missing_error_buf = std.ArrayList(InternPool.NullTerminatedString).init(gpa);
  29652     defer missing_error_buf.deinit();
  29653 
  29654     switch (src_ty.toIntern()) {
  29655         .anyerror_type => switch (ip.indexToKey(dest_ty.toIntern())) {
  29656             .simple_type => unreachable, // filtered out above
  29657             .error_set_type, .inferred_error_set_type => return .from_anyerror,
  29658             else => unreachable,
  29659         },
  29660 
  29661         else => switch (ip.indexToKey(src_ty.toIntern())) {
  29662             .inferred_error_set_type => {
  29663                 const resolved_src_ty = try sema.resolveInferredErrorSet(block, src_src, src_ty.toIntern());
  29664                 // src anyerror status might have changed after the resolution.
  29665                 if (resolved_src_ty == .anyerror_type) {
  29666                     // dest_ty.isAnyError(zcu) == true is already checked for at this point.
  29667                     return .from_anyerror;
  29668                 }
  29669 
  29670                 for (ip.indexToKey(resolved_src_ty).error_set_type.names.get(ip)) |key| {
  29671                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), key)) {
  29672                         try missing_error_buf.append(key);
  29673                     }
  29674                 }
  29675 
  29676                 if (missing_error_buf.items.len != 0) {
  29677                     return InMemoryCoercionResult{
  29678                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  29679                     };
  29680                 }
  29681 
  29682                 return .ok;
  29683             },
  29684             .error_set_type => |error_set_type| {
  29685                 for (error_set_type.names.get(ip)) |name| {
  29686                     if (!Type.errorSetHasFieldIp(ip, dest_ty.toIntern(), name)) {
  29687                         try missing_error_buf.append(name);
  29688                     }
  29689                 }
  29690 
  29691                 if (missing_error_buf.items.len != 0) {
  29692                     return InMemoryCoercionResult{
  29693                         .missing_error = try sema.arena.dupe(InternPool.NullTerminatedString, missing_error_buf.items),
  29694                     };
  29695                 }
  29696 
  29697                 return .ok;
  29698             },
  29699             else => unreachable,
  29700         },
  29701     }
  29702 }
  29703 
  29704 fn coerceInMemoryAllowedFns(
  29705     sema: *Sema,
  29706     block: *Block,
  29707     dest_ty: Type,
  29708     src_ty: Type,
  29709     /// If set, the coercion must be valid in both directions.
  29710     dest_is_mut: bool,
  29711     target: *const std.Target,
  29712     dest_src: LazySrcLoc,
  29713     src_src: LazySrcLoc,
  29714 ) !InMemoryCoercionResult {
  29715     const pt = sema.pt;
  29716     const zcu = pt.zcu;
  29717     const ip = &zcu.intern_pool;
  29718 
  29719     const dest_info = zcu.typeToFunc(dest_ty).?;
  29720     const src_info = zcu.typeToFunc(src_ty).?;
  29721 
  29722     {
  29723         if (dest_info.is_var_args != src_info.is_var_args) {
  29724             return InMemoryCoercionResult{ .fn_var_args = dest_info.is_var_args };
  29725         }
  29726 
  29727         if (dest_info.is_generic != src_info.is_generic) {
  29728             return InMemoryCoercionResult{ .fn_generic = dest_info.is_generic };
  29729         }
  29730 
  29731         const callconv_ok = callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and
  29732             (!dest_is_mut or callconvCoerceAllowed(target, dest_info.cc, src_info.cc));
  29733 
  29734         if (!callconv_ok) {
  29735             return .{ .fn_cc = .{
  29736                 .actual = src_info.cc,
  29737                 .wanted = dest_info.cc,
  29738             } };
  29739         }
  29740 
  29741         if (!switch (src_info.return_type) {
  29742             .generic_poison_type => true,
  29743             .noreturn_type => !dest_is_mut,
  29744             else => false,
  29745         }) {
  29746             const rt = try sema.coerceInMemoryAllowed(
  29747                 block,
  29748                 .fromInterned(dest_info.return_type),
  29749                 .fromInterned(src_info.return_type),
  29750                 dest_is_mut,
  29751                 target,
  29752                 dest_src,
  29753                 src_src,
  29754                 null,
  29755             );
  29756             if (rt != .ok) return .{ .fn_return_type = .{
  29757                 .child = try rt.dupe(sema.arena),
  29758                 .actual = .fromInterned(src_info.return_type),
  29759                 .wanted = .fromInterned(dest_info.return_type),
  29760             } };
  29761         }
  29762     }
  29763 
  29764     const params_len = params_len: {
  29765         if (dest_info.param_types.len != src_info.param_types.len) {
  29766             return .{ .fn_param_count = .{
  29767                 .actual = src_info.param_types.len,
  29768                 .wanted = dest_info.param_types.len,
  29769             } };
  29770         }
  29771 
  29772         if (dest_info.noalias_bits != src_info.noalias_bits) {
  29773             return .{ .fn_param_noalias = .{
  29774                 .actual = src_info.noalias_bits,
  29775                 .wanted = dest_info.noalias_bits,
  29776             } };
  29777         }
  29778 
  29779         break :params_len dest_info.param_types.len;
  29780     };
  29781 
  29782     for (0..params_len) |param_i| {
  29783         const dest_param_ty: Type = .fromInterned(dest_info.param_types.get(ip)[param_i]);
  29784         const src_param_ty: Type = .fromInterned(src_info.param_types.get(ip)[param_i]);
  29785 
  29786         comptime_param: {
  29787             const src_is_comptime = src_info.paramIsComptime(@intCast(param_i));
  29788             const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i));
  29789             if (src_is_comptime == dest_is_comptime) break :comptime_param;
  29790             if (!dest_is_mut and src_is_comptime and !dest_is_comptime and try dest_param_ty.comptimeOnlySema(pt)) {
  29791                 // A parameter which is marked `comptime` can drop that annotation if the type is comptime-only.
  29792                 // The function remains generic, and the parameter is going to be comptime-resolved either way,
  29793                 // so this just affects whether or not the argument is comptime-evaluated at the call site.
  29794                 break :comptime_param;
  29795             }
  29796             return .{ .fn_param_comptime = .{
  29797                 .index = param_i,
  29798                 .wanted = dest_is_comptime,
  29799             } };
  29800         }
  29801 
  29802         if (!src_param_ty.isGenericPoison() and !dest_param_ty.isGenericPoison()) {
  29803             // Note: Cast direction is reversed here.
  29804             const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, dest_is_mut, target, dest_src, src_src, null);
  29805             if (param != .ok) {
  29806                 return .{ .fn_param = .{
  29807                     .child = try param.dupe(sema.arena),
  29808                     .actual = src_param_ty,
  29809                     .wanted = dest_param_ty,
  29810                     .index = param_i,
  29811                 } };
  29812             }
  29813         }
  29814     }
  29815 
  29816     return .ok;
  29817 }
  29818 
  29819 fn callconvCoerceAllowed(
  29820     target: *const std.Target,
  29821     src_cc: std.builtin.CallingConvention,
  29822     dest_cc: std.builtin.CallingConvention,
  29823 ) bool {
  29824     const Tag = std.builtin.CallingConvention.Tag;
  29825     if (@as(Tag, src_cc) != @as(Tag, dest_cc)) return false;
  29826 
  29827     switch (src_cc) {
  29828         inline else => |src_data, tag| {
  29829             const dest_data = @field(dest_cc, @tagName(tag));
  29830             if (@TypeOf(src_data) != void) {
  29831                 const default_stack_align = target.stackAlignment();
  29832                 const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align;
  29833                 const dest_stack_align = src_data.incoming_stack_alignment orelse default_stack_align;
  29834                 if (dest_stack_align < src_stack_align) return false;
  29835             }
  29836             switch (@TypeOf(src_data)) {
  29837                 void, std.builtin.CallingConvention.CommonOptions => {},
  29838                 std.builtin.CallingConvention.X86RegparmOptions => {
  29839                     if (src_data.register_params != dest_data.register_params) return false;
  29840                 },
  29841                 std.builtin.CallingConvention.ArmInterruptOptions => {
  29842                     if (src_data.type != dest_data.type) return false;
  29843                 },
  29844                 std.builtin.CallingConvention.MipsInterruptOptions => {
  29845                     if (src_data.mode != dest_data.mode) return false;
  29846                 },
  29847                 std.builtin.CallingConvention.RiscvInterruptOptions => {
  29848                     if (src_data.mode != dest_data.mode) return false;
  29849                 },
  29850                 else => comptime unreachable,
  29851             }
  29852         },
  29853     }
  29854     return true;
  29855 }
  29856 
  29857 fn coerceInMemoryAllowedPtrs(
  29858     sema: *Sema,
  29859     block: *Block,
  29860     dest_ty: Type,
  29861     src_ty: Type,
  29862     dest_ptr_ty: Type,
  29863     src_ptr_ty: Type,
  29864     /// If set, the coercion must be valid in both directions.
  29865     dest_is_mut: bool,
  29866     target: *const std.Target,
  29867     dest_src: LazySrcLoc,
  29868     src_src: LazySrcLoc,
  29869 ) !InMemoryCoercionResult {
  29870     const pt = sema.pt;
  29871     const zcu = pt.zcu;
  29872     const dest_info = dest_ptr_ty.ptrInfo(zcu);
  29873     const src_info = src_ptr_ty.ptrInfo(zcu);
  29874 
  29875     const ok_ptr_size = src_info.flags.size == dest_info.flags.size or
  29876         src_info.flags.size == .c or dest_info.flags.size == .c;
  29877     if (!ok_ptr_size) {
  29878         return InMemoryCoercionResult{ .ptr_size = .{
  29879             .actual = src_info.flags.size,
  29880             .wanted = dest_info.flags.size,
  29881         } };
  29882     }
  29883 
  29884     const ok_const = src_info.flags.is_const == dest_info.flags.is_const or
  29885         (!dest_is_mut and dest_info.flags.is_const);
  29886 
  29887     if (!ok_const) return .{ .ptr_const = .{
  29888         .actual = src_ty,
  29889         .wanted = dest_ty,
  29890     } };
  29891 
  29892     const ok_volatile = src_info.flags.is_volatile == dest_info.flags.is_volatile or
  29893         (!dest_is_mut and dest_info.flags.is_volatile);
  29894 
  29895     if (!ok_volatile) return .{ .ptr_volatile = .{
  29896         .actual = src_ty,
  29897         .wanted = dest_ty,
  29898     } };
  29899 
  29900     const dest_allowzero = dest_ty.ptrAllowsZero(zcu);
  29901     const src_allowzero = src_ty.ptrAllowsZero(zcu);
  29902     const ok_allowzero = src_allowzero == dest_allowzero or
  29903         (!dest_is_mut and dest_allowzero);
  29904 
  29905     if (!ok_allowzero) return .{ .ptr_allowzero = .{
  29906         .actual = src_ty,
  29907         .wanted = dest_ty,
  29908     } };
  29909 
  29910     if (dest_info.flags.address_space != src_info.flags.address_space) {
  29911         return .{ .ptr_addrspace = .{
  29912             .actual = src_info.flags.address_space,
  29913             .wanted = dest_info.flags.address_space,
  29914         } };
  29915     }
  29916 
  29917     const dest_child: Type = .fromInterned(dest_info.child);
  29918     const src_child: Type = .fromInterned(src_info.child);
  29919     const child = try sema.coerceInMemoryAllowed(
  29920         block,
  29921         dest_child,
  29922         src_child,
  29923         // We must also include `dest_is_mut`.
  29924         // Otherwise, this code is valid:
  29925         //
  29926         // const b: B = ...;
  29927         // var pa: *const A = undefined;
  29928         // const ppa: **const A = &pa;
  29929         // const ppb: **const B = ppa; // <-- this is what that allows
  29930         // ppb.* = &b;
  29931         // const a: A = pa.*;
  29932         //
  29933         // ...effectively performing an in-memory coercion from B to A.
  29934         dest_is_mut or !dest_info.flags.is_const,
  29935         target,
  29936         dest_src,
  29937         src_src,
  29938         null,
  29939     );
  29940     if (child != .ok and !dest_is_mut) allow: {
  29941         // As a special case, we also allow coercing `*[n:s]T` to `*[n]T`, akin to dropping the sentinel from a slice.
  29942         // `*[n:s]T` cannot coerce in memory to `*[n]T` since they have different sizes.
  29943         if (src_child.zigTypeTag(zcu) == .array and dest_child.zigTypeTag(zcu) == .array and
  29944             src_child.arrayLen(zcu) == dest_child.arrayLen(zcu) and
  29945             src_child.sentinel(zcu) != null and dest_child.sentinel(zcu) == null and
  29946             .ok == try sema.coerceInMemoryAllowed(block, dest_child.childType(zcu), src_child.childType(zcu), !dest_info.flags.is_const, target, dest_src, src_src, null))
  29947         {
  29948             break :allow;
  29949         }
  29950         return .{ .ptr_child = .{
  29951             .child = try child.dupe(sema.arena),
  29952             .actual = .fromInterned(src_info.child),
  29953             .wanted = .fromInterned(dest_info.child),
  29954         } };
  29955     }
  29956 
  29957     if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or
  29958         src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset)
  29959     {
  29960         return .{ .ptr_bit_range = .{
  29961             .actual_host = src_info.packed_offset.host_size,
  29962             .wanted_host = dest_info.packed_offset.host_size,
  29963             .actual_offset = src_info.packed_offset.bit_offset,
  29964             .wanted_offset = dest_info.packed_offset.bit_offset,
  29965         } };
  29966     }
  29967 
  29968     const sentinel_ok = ok: {
  29969         const ss = src_info.sentinel;
  29970         const ds = dest_info.sentinel;
  29971         if (ss == .none and ds == .none) break :ok true;
  29972         if (ss != .none and ds != .none) {
  29973             if (ds == try zcu.intern_pool.getCoerced(sema.gpa, pt.tid, ss, dest_info.child)) break :ok true;
  29974         }
  29975         if (src_info.flags.size == .c) break :ok true;
  29976         if (!dest_is_mut and dest_info.sentinel == .none) break :ok true;
  29977         break :ok false;
  29978     };
  29979 
  29980     if (!sentinel_ok) {
  29981         return .{ .ptr_sentinel = .{
  29982             .actual = switch (src_info.sentinel) {
  29983                 .none => Value.@"unreachable",
  29984                 else => Value.fromInterned(src_info.sentinel),
  29985             },
  29986             .wanted = switch (dest_info.sentinel) {
  29987                 .none => Value.@"unreachable",
  29988                 else => Value.fromInterned(dest_info.sentinel),
  29989             },
  29990             .ty = .fromInterned(dest_info.child),
  29991         } };
  29992     }
  29993 
  29994     // If both pointers have alignment 0, it means they both want ABI alignment.
  29995     // In this case, if they share the same child type, no need to resolve
  29996     // pointee type alignment. Otherwise both pointee types must have their alignment
  29997     // resolved and we compare the alignment numerically.
  29998     if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or
  29999         dest_info.child != src_info.child)
  30000     {
  30001         const src_align = if (src_info.flags.alignment != .none)
  30002             src_info.flags.alignment
  30003         else
  30004             try Type.fromInterned(src_info.child).abiAlignmentSema(pt);
  30005 
  30006         const dest_align = if (dest_info.flags.alignment != .none)
  30007             dest_info.flags.alignment
  30008         else
  30009             try Type.fromInterned(dest_info.child).abiAlignmentSema(pt);
  30010 
  30011         if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) {
  30012             return InMemoryCoercionResult{ .ptr_alignment = .{
  30013                 .actual = src_align,
  30014                 .wanted = dest_align,
  30015             } };
  30016         }
  30017     }
  30018 
  30019     return .ok;
  30020 }
  30021 
  30022 fn coerceVarArgParam(
  30023     sema: *Sema,
  30024     block: *Block,
  30025     inst: Air.Inst.Ref,
  30026     inst_src: LazySrcLoc,
  30027 ) !Air.Inst.Ref {
  30028     if (block.is_typeof) return inst;
  30029 
  30030     const pt = sema.pt;
  30031     const zcu = pt.zcu;
  30032     const uncasted_ty = sema.typeOf(inst);
  30033     const coerced = switch (uncasted_ty.zigTypeTag(zcu)) {
  30034         // TODO consider casting to c_int/f64 if they fit
  30035         .comptime_int, .comptime_float => return sema.fail(
  30036             block,
  30037             inst_src,
  30038             "integer and float literals passed to variadic function must be casted to a fixed-size number type",
  30039             .{},
  30040         ),
  30041         .@"fn" => fn_ptr: {
  30042             const fn_val = try sema.resolveConstDefinedValue(block, LazySrcLoc.unneeded, inst, undefined);
  30043             const fn_nav = zcu.funcInfo(fn_val.toIntern()).owner_nav;
  30044             break :fn_ptr try sema.analyzeNavRef(block, inst_src, fn_nav);
  30045         },
  30046         .array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
  30047         .float => float: {
  30048             const target = zcu.getTarget();
  30049             const double_bits = target.cTypeBitSize(.double);
  30050             const inst_bits = uncasted_ty.floatBits(target);
  30051             if (inst_bits >= double_bits) break :float inst;
  30052             switch (double_bits) {
  30053                 32 => break :float try sema.coerce(block, .f32, inst, inst_src),
  30054                 64 => break :float try sema.coerce(block, .f64, inst, inst_src),
  30055                 else => unreachable,
  30056             }
  30057         },
  30058         else => if (uncasted_ty.isAbiInt(zcu)) int: {
  30059             if (!try sema.validateExternType(uncasted_ty, .param_ty)) break :int inst;
  30060             const target = zcu.getTarget();
  30061             const uncasted_info = uncasted_ty.intInfo(zcu);
  30062             if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) {
  30063                 .signed => .int,
  30064                 .unsigned => .uint,
  30065             })) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
  30066                 .signed => .c_int,
  30067                 .unsigned => .c_uint,
  30068             }, inst, inst_src);
  30069             if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) {
  30070                 .signed => .long,
  30071                 .unsigned => .ulong,
  30072             })) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
  30073                 .signed => .c_long,
  30074                 .unsigned => .c_ulong,
  30075             }, inst, inst_src);
  30076             if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) {
  30077                 .signed => .longlong,
  30078                 .unsigned => .ulonglong,
  30079             })) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
  30080                 .signed => .c_longlong,
  30081                 .unsigned => .c_ulonglong,
  30082             }, inst, inst_src);
  30083             break :int inst;
  30084         } else inst,
  30085     };
  30086 
  30087     const coerced_ty = sema.typeOf(coerced);
  30088     if (!try sema.validateExternType(coerced_ty, .param_ty)) {
  30089         const msg = msg: {
  30090             const msg = try sema.errMsg(inst_src, "cannot pass '{f}' to variadic function", .{coerced_ty.fmt(pt)});
  30091             errdefer msg.destroy(sema.gpa);
  30092 
  30093             try sema.explainWhyTypeIsNotExtern(msg, inst_src, coerced_ty, .param_ty);
  30094 
  30095             try sema.addDeclaredHereNote(msg, coerced_ty);
  30096             break :msg msg;
  30097         };
  30098         return sema.failWithOwnedErrorMsg(block, msg);
  30099     }
  30100     return coerced;
  30101 }
  30102 
  30103 // TODO migrate callsites to use storePtr2 instead.
  30104 fn storePtr(
  30105     sema: *Sema,
  30106     block: *Block,
  30107     src: LazySrcLoc,
  30108     ptr: Air.Inst.Ref,
  30109     uncasted_operand: Air.Inst.Ref,
  30110 ) CompileError!void {
  30111     const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store;
  30112     return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag);
  30113 }
  30114 
  30115 fn storePtr2(
  30116     sema: *Sema,
  30117     block: *Block,
  30118     src: LazySrcLoc,
  30119     ptr: Air.Inst.Ref,
  30120     ptr_src: LazySrcLoc,
  30121     uncasted_operand: Air.Inst.Ref,
  30122     operand_src: LazySrcLoc,
  30123     air_tag: Air.Inst.Tag,
  30124 ) CompileError!void {
  30125     const pt = sema.pt;
  30126     const zcu = pt.zcu;
  30127     const ptr_ty = sema.typeOf(ptr);
  30128     if (ptr_ty.isConstPtr(zcu))
  30129         return sema.fail(block, ptr_src, "cannot assign to constant", .{});
  30130 
  30131     const elem_ty = ptr_ty.childType(zcu);
  30132 
  30133     // To generate better code for tuples, we detect a tuple operand here, and
  30134     // analyze field loads and stores directly. This avoids an extra allocation + memcpy
  30135     // which would occur if we used `coerce`.
  30136     // However, we avoid this mechanism if the destination element type is a tuple,
  30137     // because the regular store will be better for this case.
  30138     // If the destination type is a struct we don't want this mechanism to trigger, because
  30139     // this code does not handle tuple-to-struct coercion which requires dealing with missing
  30140     // fields.
  30141     const operand_ty = sema.typeOf(uncasted_operand);
  30142     if (operand_ty.isTuple(zcu) and elem_ty.zigTypeTag(zcu) == .array) {
  30143         const field_count = operand_ty.structFieldCount(zcu);
  30144         var i: u32 = 0;
  30145         while (i < field_count) : (i += 1) {
  30146             const elem_src = operand_src; // TODO better source location
  30147             const elem = try sema.tupleField(block, operand_src, uncasted_operand, elem_src, i);
  30148             const elem_index = try pt.intRef(.usize, i);
  30149             const elem_ptr = try sema.elemPtr(block, ptr_src, ptr, elem_index, elem_src, false, true);
  30150             try sema.storePtr2(block, src, elem_ptr, elem_src, elem, elem_src, .store);
  30151         }
  30152         return;
  30153     }
  30154 
  30155     // TODO do the same thing for anon structs as for tuples above.
  30156     // However, beware of the need to handle missing/extra fields.
  30157 
  30158     const is_ret = air_tag == .ret_ptr;
  30159 
  30160     // Detect if we are storing an array operand to a bitcasted vector pointer.
  30161     // If so, we instead reach through the bitcasted pointer to the vector pointer,
  30162     // bitcast the array operand to a vector, and then lower this as a store of
  30163     // a vector value to a vector pointer. This generally results in better code,
  30164     // as well as working around an LLVM bug:
  30165     // https://github.com/ziglang/zig/issues/11154
  30166     if (sema.obtainBitCastedVectorPtr(ptr)) |vector_ptr| {
  30167         const vector_ty = sema.typeOf(vector_ptr).childType(zcu);
  30168         const vector = sema.coerceExtra(block, vector_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  30169             error.NotCoercible => unreachable,
  30170             else => |e| return e,
  30171         };
  30172         try sema.storePtr2(block, src, vector_ptr, ptr_src, vector, operand_src, .store);
  30173         return;
  30174     }
  30175 
  30176     const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
  30177         error.NotCoercible => unreachable,
  30178         else => |e| return e,
  30179     };
  30180     const maybe_operand_val = try sema.resolveValue(operand);
  30181 
  30182     const runtime_src = rs: {
  30183         const ptr_val = try sema.resolveDefinedValue(block, ptr_src, ptr) orelse break :rs ptr_src;
  30184         if (!sema.isComptimeMutablePtr(ptr_val)) break :rs ptr_src;
  30185         const operand_val = maybe_operand_val orelse return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{});
  30186         return sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
  30187     };
  30188 
  30189     // We're performing the store at runtime; as such, we need to make sure the pointee type
  30190     // is not comptime-only. We can hit this case with a `@ptrFromInt` pointer.
  30191     if (try elem_ty.comptimeOnlySema(pt)) {
  30192         return sema.failWithOwnedErrorMsg(block, msg: {
  30193             const msg = try sema.errMsg(src, "cannot store comptime-only type '{f}' at runtime", .{elem_ty.fmt(pt)});
  30194             errdefer msg.destroy(sema.gpa);
  30195             try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{});
  30196             break :msg msg;
  30197         });
  30198     }
  30199 
  30200     // We do this after the possible comptime store above, for the case of field_ptr stores
  30201     // to unions because we want the comptime tag to be set, even if the field type is void.
  30202     if ((try sema.typeHasOnePossibleValue(elem_ty)) != null) {
  30203         return;
  30204     }
  30205 
  30206     try sema.requireRuntimeBlock(block, src, runtime_src);
  30207 
  30208     if (ptr_ty.ptrInfo(zcu).flags.vector_index == .runtime) {
  30209         const ptr_inst = ptr.toIndex().?;
  30210         const air_tags = sema.air_instructions.items(.tag);
  30211         if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) {
  30212             const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl;
  30213             const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
  30214             _ = try block.addInst(.{
  30215                 .tag = .vector_store_elem,
  30216                 .data = .{ .vector_store_elem = .{
  30217                     .vector_ptr = bin_op.lhs,
  30218                     .payload = try block.sema.addExtra(Air.Bin{
  30219                         .lhs = bin_op.rhs,
  30220                         .rhs = operand,
  30221                     }),
  30222                 } },
  30223             });
  30224             return;
  30225         }
  30226         return sema.fail(block, ptr_src, "unable to determine vector element index of type '{f}'", .{
  30227             ptr_ty.fmt(pt),
  30228         });
  30229     }
  30230 
  30231     const store_inst = if (is_ret)
  30232         try block.addBinOp(.store, ptr, operand)
  30233     else
  30234         try block.addBinOp(air_tag, ptr, operand);
  30235 
  30236     try sema.checkComptimeKnownStore(block, store_inst, operand_src);
  30237 
  30238     return;
  30239 }
  30240 
  30241 /// Given an AIR store instruction, checks whether we are performing a
  30242 /// comptime-known store to a local alloc, and updates `maybe_comptime_allocs`
  30243 /// accordingly.
  30244 /// Handles calling `validateRuntimeValue` if the store is runtime for any reason.
  30245 fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst.Ref, store_src: LazySrcLoc) !void {
  30246     const store_inst = store_inst_ref.toIndex().?;
  30247     const inst_data = sema.air_instructions.items(.data)[@intFromEnum(store_inst)].bin_op;
  30248     const ptr = inst_data.lhs.toIndex() orelse return;
  30249     const operand = inst_data.rhs;
  30250 
  30251     known: {
  30252         const maybe_base_alloc = sema.base_allocs.get(ptr) orelse break :known;
  30253         const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse break :known;
  30254 
  30255         if ((try sema.resolveValue(operand)) != null and
  30256             block.runtime_index == maybe_comptime_alloc.runtime_index)
  30257         {
  30258             try maybe_comptime_alloc.stores.append(sema.arena, .{
  30259                 .inst = store_inst,
  30260                 .src = store_src,
  30261             });
  30262             return;
  30263         }
  30264 
  30265         // We're newly discovering that this alloc is runtime-known.
  30266         try sema.markMaybeComptimeAllocRuntime(block, maybe_base_alloc);
  30267     }
  30268 
  30269     try sema.validateRuntimeValue(block, store_src, operand);
  30270 }
  30271 
  30272 /// Given an AIR instruction transforming a pointer (struct_field_ptr,
  30273 /// ptr_elem_ptr, bitcast, etc), checks whether the base pointer refers to a
  30274 /// local alloc, and updates `base_allocs` accordingly.
  30275 fn checkKnownAllocPtr(sema: *Sema, block: *Block, base_ptr: Air.Inst.Ref, new_ptr: Air.Inst.Ref) !void {
  30276     const base_ptr_inst = base_ptr.toIndex() orelse return;
  30277     const new_ptr_inst = new_ptr.toIndex() orelse return;
  30278     const alloc_inst = sema.base_allocs.get(base_ptr_inst) orelse return;
  30279     try sema.base_allocs.put(sema.gpa, new_ptr_inst, alloc_inst);
  30280 
  30281     switch (sema.air_instructions.items(.tag)[@intFromEnum(new_ptr_inst)]) {
  30282         .optional_payload_ptr_set, .errunion_payload_ptr_set => {
  30283             const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(alloc_inst) orelse return;
  30284 
  30285             // This is functionally a store, since it writes the optional payload bit.
  30286             // Thus, if it is behind a runtime condition, we must mark the alloc as runtime appropriately.
  30287             if (block.runtime_index != maybe_comptime_alloc.runtime_index) {
  30288                 return sema.markMaybeComptimeAllocRuntime(block, alloc_inst);
  30289             }
  30290 
  30291             try maybe_comptime_alloc.stores.append(sema.arena, .{
  30292                 .inst = new_ptr_inst,
  30293                 .src = LazySrcLoc.unneeded,
  30294             });
  30295         },
  30296         .ptr_elem_ptr => {
  30297             const tmp_air = sema.getTmpAir();
  30298             const pl_idx = tmp_air.instructions.items(.data)[@intFromEnum(new_ptr_inst)].ty_pl.payload;
  30299             const bin = tmp_air.extraData(Air.Bin, pl_idx).data;
  30300             const index_ref = bin.rhs;
  30301 
  30302             // If the index value is runtime-known, this pointer is also runtime-known, so
  30303             // we must in turn make the alloc value runtime-known.
  30304             if (null == try sema.resolveValue(index_ref)) {
  30305                 try sema.markMaybeComptimeAllocRuntime(block, alloc_inst);
  30306             }
  30307         },
  30308         else => {},
  30309     }
  30310 }
  30311 
  30312 fn markMaybeComptimeAllocRuntime(sema: *Sema, block: *Block, alloc_inst: Air.Inst.Index) CompileError!void {
  30313     const maybe_comptime_alloc = (sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return).value;
  30314     // Since the alloc has been determined to be runtime, we must check that
  30315     // all other stores to it are permitted to be runtime values.
  30316     const slice = maybe_comptime_alloc.stores.slice();
  30317     for (slice.items(.inst), slice.items(.src)) |other_inst, other_src| {
  30318         if (other_src.offset == .unneeded) {
  30319             switch (sema.air_instructions.items(.tag)[@intFromEnum(other_inst)]) {
  30320                 .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => continue,
  30321                 else => unreachable, // assertion failure
  30322             }
  30323         }
  30324         const other_data = sema.air_instructions.items(.data)[@intFromEnum(other_inst)].bin_op;
  30325         const other_operand = other_data.rhs;
  30326         try sema.validateRuntimeValue(block, other_src, other_operand);
  30327     }
  30328 }
  30329 
  30330 /// Traverse an arbitrary number of bitcasted pointers and return the underyling vector
  30331 /// pointer. Only if the final element type matches the vector element type, and the
  30332 /// lengths match.
  30333 fn obtainBitCastedVectorPtr(sema: *Sema, ptr: Air.Inst.Ref) ?Air.Inst.Ref {
  30334     const pt = sema.pt;
  30335     const zcu = pt.zcu;
  30336     const array_ty = sema.typeOf(ptr).childType(zcu);
  30337     if (array_ty.zigTypeTag(zcu) != .array) return null;
  30338     var ptr_ref = ptr;
  30339     var ptr_inst = ptr_ref.toIndex() orelse return null;
  30340     const air_datas = sema.air_instructions.items(.data);
  30341     const air_tags = sema.air_instructions.items(.tag);
  30342     const vector_ty = while (air_tags[@intFromEnum(ptr_inst)] == .bitcast) {
  30343         ptr_ref = air_datas[@intFromEnum(ptr_inst)].ty_op.operand;
  30344         if (!sema.isKnownZigType(ptr_ref, .pointer)) return null;
  30345         const child_ty = sema.typeOf(ptr_ref).childType(zcu);
  30346         if (child_ty.zigTypeTag(zcu) == .vector) break child_ty;
  30347         ptr_inst = ptr_ref.toIndex() orelse return null;
  30348     } else return null;
  30349 
  30350     // We have a pointer-to-array and a pointer-to-vector. If the elements and
  30351     // lengths match, return the result.
  30352     if (array_ty.childType(zcu).eql(vector_ty.childType(zcu), zcu) and
  30353         array_ty.arrayLen(zcu) == vector_ty.vectorLen(zcu))
  30354     {
  30355         return ptr_ref;
  30356     } else {
  30357         return null;
  30358     }
  30359 }
  30360 
  30361 /// Call when you have Value objects rather than Air instructions, and you want to
  30362 /// assert the store must be done at comptime.
  30363 fn storePtrVal(
  30364     sema: *Sema,
  30365     block: *Block,
  30366     src: LazySrcLoc,
  30367     ptr_val: Value,
  30368     operand_val: Value,
  30369     operand_ty: Type,
  30370 ) !void {
  30371     const pt = sema.pt;
  30372     const zcu = pt.zcu;
  30373     const ip = &zcu.intern_pool;
  30374     // TODO: audit use sites to eliminate this coercion
  30375     const coerced_operand_val = try pt.getCoerced(operand_val, operand_ty);
  30376     // TODO: audit use sites to eliminate this coercion
  30377     const ptr_ty = try pt.ptrType(info: {
  30378         var info = ptr_val.typeOf(zcu).ptrInfo(zcu);
  30379         info.child = operand_ty.toIntern();
  30380         break :info info;
  30381     });
  30382     const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty);
  30383 
  30384     switch (try sema.storeComptimePtr(block, src, coerced_ptr_val, coerced_operand_val)) {
  30385         .success => {},
  30386         .runtime_store => unreachable, // use sites check this
  30387         // TODO use failWithInvalidComptimeFieldStore
  30388         .comptime_field_mismatch => return sema.fail(
  30389             block,
  30390             src,
  30391             "value stored in comptime field does not match the default value of the field",
  30392             .{},
  30393         ),
  30394         .undef => return sema.failWithUseOfUndef(block, src),
  30395         .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}),
  30396         .null_payload => return sema.fail(block, src, "attempt to use null value", .{}),
  30397         .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}),
  30398         .needed_well_defined => |ty| return sema.fail(
  30399             block,
  30400             src,
  30401             "comptime dereference requires '{f}' to have a well-defined layout",
  30402             .{ty.fmt(pt)},
  30403         ),
  30404         .out_of_bounds => |ty| return sema.fail(
  30405             block,
  30406             src,
  30407             "dereference of '{f}' exceeds bounds of containing decl of type '{f}'",
  30408             .{ ptr_ty.fmt(pt), ty.fmt(pt) },
  30409         ),
  30410         .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}),
  30411     }
  30412 }
  30413 
  30414 fn bitCast(
  30415     sema: *Sema,
  30416     block: *Block,
  30417     dest_ty: Type,
  30418     inst: Air.Inst.Ref,
  30419     inst_src: LazySrcLoc,
  30420     operand_src: ?LazySrcLoc,
  30421 ) CompileError!Air.Inst.Ref {
  30422     const pt = sema.pt;
  30423     const zcu = pt.zcu;
  30424     try dest_ty.resolveLayout(pt);
  30425 
  30426     const old_ty = sema.typeOf(inst);
  30427     try old_ty.resolveLayout(pt);
  30428 
  30429     const dest_bits = dest_ty.bitSize(zcu);
  30430     const old_bits = old_ty.bitSize(zcu);
  30431 
  30432     if (old_bits != dest_bits) {
  30433         return sema.fail(block, inst_src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{
  30434             dest_ty.fmt(pt),
  30435             dest_bits,
  30436             old_ty.fmt(pt),
  30437             old_bits,
  30438         });
  30439     }
  30440 
  30441     if (try sema.resolveValue(inst)) |val| {
  30442         if (val.isUndef(zcu))
  30443             return pt.undefRef(dest_ty);
  30444         if (old_ty.zigTypeTag(zcu) == .error_set and dest_ty.zigTypeTag(zcu) == .error_set) {
  30445             // Special case: we sometimes call `bitCast` on error set values, but they
  30446             // don't have a well-defined layout, so we can't use `bitCastVal` on them.
  30447             return Air.internedToRef((try pt.getCoerced(val, dest_ty)).toIntern());
  30448         }
  30449         if (try sema.bitCastVal(val, dest_ty, 0, 0, 0)) |result_val| {
  30450             return Air.internedToRef(result_val.toIntern());
  30451         }
  30452     }
  30453     try sema.requireRuntimeBlock(block, inst_src, operand_src);
  30454     try sema.validateRuntimeValue(block, inst_src, inst);
  30455     return block.addBitCast(dest_ty, inst);
  30456 }
  30457 
  30458 fn coerceArrayPtrToSlice(
  30459     sema: *Sema,
  30460     block: *Block,
  30461     dest_ty: Type,
  30462     inst: Air.Inst.Ref,
  30463     inst_src: LazySrcLoc,
  30464 ) CompileError!Air.Inst.Ref {
  30465     const pt = sema.pt;
  30466     const zcu = pt.zcu;
  30467     if (try sema.resolveValue(inst)) |val| {
  30468         const ptr_array_ty = sema.typeOf(inst);
  30469         const array_ty = ptr_array_ty.childType(zcu);
  30470         const slice_ptr_ty = dest_ty.slicePtrFieldType(zcu);
  30471         const slice_ptr = try pt.getCoerced(val, slice_ptr_ty);
  30472         const slice_val = try pt.intern(.{ .slice = .{
  30473             .ty = dest_ty.toIntern(),
  30474             .ptr = slice_ptr.toIntern(),
  30475             .len = (try pt.intValue(.usize, array_ty.arrayLen(zcu))).toIntern(),
  30476         } });
  30477         return Air.internedToRef(slice_val);
  30478     }
  30479     try sema.requireRuntimeBlock(block, inst_src, null);
  30480     return block.addTyOp(.array_to_slice, dest_ty, inst);
  30481 }
  30482 
  30483 fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool {
  30484     const pt = sema.pt;
  30485     const zcu = pt.zcu;
  30486     const dest_info = dest_ty.ptrInfo(zcu);
  30487     const inst_info = inst_ty.ptrInfo(zcu);
  30488     const len0 = (Type.fromInterned(inst_info.child).zigTypeTag(zcu) == .array and (Type.fromInterned(inst_info.child).arrayLenIncludingSentinel(zcu) == 0 or
  30489         (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
  30490         (Type.fromInterned(inst_info.child).isTuple(zcu) and Type.fromInterned(inst_info.child).structFieldCount(zcu) == 0);
  30491 
  30492     const ok_const = (!inst_info.flags.is_const or dest_info.flags.is_const) or len0;
  30493     const ok_volatile = !inst_info.flags.is_volatile or dest_info.flags.is_volatile;
  30494     if (!ok_const) {
  30495         in_memory_result.* = .{ .ptr_const = .{
  30496             .actual = inst_ty,
  30497             .wanted = dest_ty,
  30498         } };
  30499         return false;
  30500     }
  30501     if (!ok_volatile) {
  30502         in_memory_result.* = .{ .ptr_volatile = .{
  30503             .actual = inst_ty,
  30504             .wanted = dest_ty,
  30505         } };
  30506         return false;
  30507     }
  30508 
  30509     if (dest_info.flags.address_space != inst_info.flags.address_space) {
  30510         in_memory_result.* = .{ .ptr_addrspace = .{
  30511             .actual = inst_info.flags.address_space,
  30512             .wanted = dest_info.flags.address_space,
  30513         } };
  30514         return false;
  30515     }
  30516     if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true;
  30517     if (len0) return true;
  30518 
  30519     const inst_align = if (inst_info.flags.alignment != .none)
  30520         inst_info.flags.alignment
  30521     else
  30522         Type.fromInterned(inst_info.child).abiAlignment(zcu);
  30523 
  30524     const dest_align = if (dest_info.flags.alignment != .none)
  30525         dest_info.flags.alignment
  30526     else
  30527         Type.fromInterned(dest_info.child).abiAlignment(zcu);
  30528 
  30529     if (dest_align.compare(.gt, inst_align)) {
  30530         in_memory_result.* = .{ .ptr_alignment = .{
  30531             .actual = inst_align,
  30532             .wanted = dest_align,
  30533         } };
  30534         return false;
  30535     }
  30536     return true;
  30537 }
  30538 
  30539 fn coerceCompatiblePtrs(
  30540     sema: *Sema,
  30541     block: *Block,
  30542     dest_ty: Type,
  30543     inst: Air.Inst.Ref,
  30544     inst_src: LazySrcLoc,
  30545 ) !Air.Inst.Ref {
  30546     const pt = sema.pt;
  30547     const zcu = pt.zcu;
  30548     const inst_ty = sema.typeOf(inst);
  30549     if (try sema.resolveValue(inst)) |val| {
  30550         if (!val.isUndef(zcu) and val.isNull(zcu) and !dest_ty.isAllowzeroPtr(zcu)) {
  30551             return sema.fail(block, inst_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)});
  30552         }
  30553         // The comptime Value representation is compatible with both types.
  30554         return Air.internedToRef(
  30555             (try pt.getCoerced(val, dest_ty)).toIntern(),
  30556         );
  30557     }
  30558     try sema.requireRuntimeBlock(block, inst_src, null);
  30559     const inst_allows_zero = inst_ty.zigTypeTag(zcu) != .pointer or inst_ty.ptrAllowsZero(zcu);
  30560     if (block.wantSafety() and inst_allows_zero and !dest_ty.ptrAllowsZero(zcu) and
  30561         (try dest_ty.elemType2(zcu).hasRuntimeBitsSema(pt) or dest_ty.elemType2(zcu).zigTypeTag(zcu) == .@"fn"))
  30562     {
  30563         try sema.checkLogicalPtrOperation(block, inst_src, inst_ty);
  30564         const actual_ptr = if (inst_ty.isSlice(zcu))
  30565             try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
  30566         else
  30567             inst;
  30568         const ptr_int = try block.addBitCast(.usize, actual_ptr);
  30569         const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
  30570         const ok = if (inst_ty.isSlice(zcu)) ok: {
  30571             const len = try sema.analyzeSliceLen(block, inst_src, inst);
  30572             const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
  30573             break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero);
  30574         } else is_non_zero;
  30575         try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null);
  30576     }
  30577     const new_ptr = try sema.bitCast(block, dest_ty, inst, inst_src, null);
  30578     try sema.checkKnownAllocPtr(block, inst, new_ptr);
  30579     return new_ptr;
  30580 }
  30581 
  30582 fn coerceEnumToUnion(
  30583     sema: *Sema,
  30584     block: *Block,
  30585     union_ty: Type,
  30586     union_ty_src: LazySrcLoc,
  30587     inst: Air.Inst.Ref,
  30588     inst_src: LazySrcLoc,
  30589 ) !Air.Inst.Ref {
  30590     const pt = sema.pt;
  30591     const zcu = pt.zcu;
  30592     const ip = &zcu.intern_pool;
  30593     const inst_ty = sema.typeOf(inst);
  30594 
  30595     const tag_ty = union_ty.unionTagType(zcu) orelse {
  30596         const msg = msg: {
  30597             const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{
  30598                 union_ty.fmt(pt), inst_ty.fmt(pt),
  30599             });
  30600             errdefer msg.destroy(sema.gpa);
  30601             try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{});
  30602             try sema.addDeclaredHereNote(msg, union_ty);
  30603             break :msg msg;
  30604         };
  30605         return sema.failWithOwnedErrorMsg(block, msg);
  30606     };
  30607 
  30608     const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
  30609     if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
  30610         const field_index = union_ty.unionTagFieldIndex(val, pt.zcu) orelse {
  30611             return sema.fail(block, inst_src, "union '{f}' has no tag with value '{f}'", .{
  30612                 union_ty.fmt(pt), val.fmtValueSema(pt, sema),
  30613             });
  30614         };
  30615 
  30616         const union_obj = zcu.typeToUnion(union_ty).?;
  30617         const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  30618         try field_ty.resolveFields(pt);
  30619         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  30620             const msg = msg: {
  30621                 const msg = try sema.errMsg(inst_src, "cannot initialize 'noreturn' field of union", .{});
  30622                 errdefer msg.destroy(sema.gpa);
  30623 
  30624                 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index];
  30625                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
  30626                     field_name.fmt(ip),
  30627                 });
  30628                 try sema.addDeclaredHereNote(msg, union_ty);
  30629                 break :msg msg;
  30630             };
  30631             return sema.failWithOwnedErrorMsg(block, msg);
  30632         }
  30633         const opv = (try sema.typeHasOnePossibleValue(field_ty)) orelse {
  30634             const msg = msg: {
  30635                 const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index];
  30636                 const msg = try sema.errMsg(inst_src, "coercion from enum '{f}' to union '{f}' must initialize '{f}' field '{f}'", .{
  30637                     inst_ty.fmt(pt),  union_ty.fmt(pt),
  30638                     field_ty.fmt(pt), field_name.fmt(ip),
  30639                 });
  30640                 errdefer msg.destroy(sema.gpa);
  30641 
  30642                 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
  30643                     field_name.fmt(ip),
  30644                 });
  30645                 try sema.addDeclaredHereNote(msg, union_ty);
  30646                 break :msg msg;
  30647             };
  30648             return sema.failWithOwnedErrorMsg(block, msg);
  30649         };
  30650 
  30651         return Air.internedToRef((try pt.unionValue(union_ty, val, opv)).toIntern());
  30652     }
  30653 
  30654     try sema.requireRuntimeBlock(block, inst_src, null);
  30655 
  30656     if (tag_ty.isNonexhaustiveEnum(zcu)) {
  30657         const msg = msg: {
  30658             const msg = try sema.errMsg(inst_src, "runtime coercion to union '{f}' from non-exhaustive enum", .{
  30659                 union_ty.fmt(pt),
  30660             });
  30661             errdefer msg.destroy(sema.gpa);
  30662             try sema.addDeclaredHereNote(msg, tag_ty);
  30663             break :msg msg;
  30664         };
  30665         return sema.failWithOwnedErrorMsg(block, msg);
  30666     }
  30667 
  30668     const union_obj = zcu.typeToUnion(union_ty).?;
  30669     {
  30670         var msg: ?*Zcu.ErrorMsg = null;
  30671         errdefer if (msg) |some| some.destroy(sema.gpa);
  30672 
  30673         for (union_obj.field_types.get(ip), 0..) |field_ty, field_index| {
  30674             if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .noreturn) {
  30675                 const err_msg = msg orelse try sema.errMsg(
  30676                     inst_src,
  30677                     "runtime coercion from enum '{f}' to union '{f}' which has a 'noreturn' field",
  30678                     .{ tag_ty.fmt(pt), union_ty.fmt(pt) },
  30679                 );
  30680                 msg = err_msg;
  30681 
  30682                 try sema.addFieldErrNote(union_ty, field_index, err_msg, "'noreturn' field here", .{});
  30683             }
  30684         }
  30685         if (msg) |some| {
  30686             msg = null;
  30687             try sema.addDeclaredHereNote(some, union_ty);
  30688             return sema.failWithOwnedErrorMsg(block, some);
  30689         }
  30690     }
  30691 
  30692     // If the union has all fields 0 bits, the union value is just the enum value.
  30693     if (union_ty.unionHasAllZeroBitFieldTypes(zcu)) {
  30694         return block.addBitCast(union_ty, enum_tag);
  30695     }
  30696 
  30697     const msg = msg: {
  30698         const msg = try sema.errMsg(
  30699             inst_src,
  30700             "runtime coercion from enum '{f}' to union '{f}' which has non-void fields",
  30701             .{ tag_ty.fmt(pt), union_ty.fmt(pt) },
  30702         );
  30703         errdefer msg.destroy(sema.gpa);
  30704 
  30705         for (0..union_obj.field_types.len) |field_index| {
  30706             const field_name = union_obj.loadTagType(ip).names.get(ip)[field_index];
  30707             const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  30708             if (!(try field_ty.hasRuntimeBitsSema(pt))) continue;
  30709             try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' has type '{f}'", .{
  30710                 field_name.fmt(ip),
  30711                 field_ty.fmt(pt),
  30712             });
  30713         }
  30714         try sema.addDeclaredHereNote(msg, union_ty);
  30715         break :msg msg;
  30716     };
  30717     return sema.failWithOwnedErrorMsg(block, msg);
  30718 }
  30719 
  30720 /// If the lengths match, coerces element-wise.
  30721 fn coerceArrayLike(
  30722     sema: *Sema,
  30723     block: *Block,
  30724     dest_ty: Type,
  30725     dest_ty_src: LazySrcLoc,
  30726     inst: Air.Inst.Ref,
  30727     inst_src: LazySrcLoc,
  30728 ) !Air.Inst.Ref {
  30729     const pt = sema.pt;
  30730     const zcu = pt.zcu;
  30731     const inst_ty = sema.typeOf(inst);
  30732     const target = zcu.getTarget();
  30733 
  30734     // try coercion of the whole array
  30735     const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, null);
  30736     if (in_memory_result == .ok) {
  30737         if (try sema.resolveValue(inst)) |inst_val| {
  30738             // These types share the same comptime value representation.
  30739             return sema.coerceInMemory(inst_val, dest_ty);
  30740         }
  30741         try sema.requireRuntimeBlock(block, inst_src, null);
  30742         return block.addBitCast(dest_ty, inst);
  30743     }
  30744 
  30745     // otherwise, try element by element
  30746     const inst_len = inst_ty.arrayLen(zcu);
  30747     const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu));
  30748     if (dest_len != inst_len) {
  30749         const msg = msg: {
  30750             const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{
  30751                 dest_ty.fmt(pt), inst_ty.fmt(pt),
  30752             });
  30753             errdefer msg.destroy(sema.gpa);
  30754             try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
  30755             try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
  30756             break :msg msg;
  30757         };
  30758         return sema.failWithOwnedErrorMsg(block, msg);
  30759     }
  30760 
  30761     const dest_elem_ty = dest_ty.childType(zcu);
  30762     if (dest_ty.isVector(zcu) and inst_ty.isVector(zcu) and (try sema.resolveValue(inst)) == null) {
  30763         const inst_elem_ty = inst_ty.childType(zcu);
  30764         switch (dest_elem_ty.zigTypeTag(zcu)) {
  30765             .int => if (inst_elem_ty.isInt(zcu)) {
  30766                 // integer widening
  30767                 const dst_info = dest_elem_ty.intInfo(zcu);
  30768                 const src_info = inst_elem_ty.intInfo(zcu);
  30769                 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
  30770                     // small enough unsigned ints can get casted to large enough signed ints
  30771                     (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
  30772                 {
  30773                     try sema.requireRuntimeBlock(block, inst_src, null);
  30774                     return block.addTyOp(.intcast, dest_ty, inst);
  30775                 }
  30776             },
  30777             .float => if (inst_elem_ty.isRuntimeFloat()) {
  30778                 // float widening
  30779                 const src_bits = inst_elem_ty.floatBits(target);
  30780                 const dst_bits = dest_elem_ty.floatBits(target);
  30781                 if (dst_bits >= src_bits) {
  30782                     try sema.requireRuntimeBlock(block, inst_src, null);
  30783                     return block.addTyOp(.fpext, dest_ty, inst);
  30784                 }
  30785             },
  30786             else => {},
  30787         }
  30788     }
  30789 
  30790     const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
  30791     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
  30792     var runtime_src: ?LazySrcLoc = null;
  30793 
  30794     for (element_vals, element_refs, 0..) |*val, *ref, i| {
  30795         const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern());
  30796         const src = inst_src; // TODO better source location
  30797         const elem_src = inst_src; // TODO better source location
  30798         const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true);
  30799         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  30800         ref.* = coerced;
  30801         if (runtime_src == null) {
  30802             if (try sema.resolveValue(coerced)) |elem_val| {
  30803                 val.* = elem_val.toIntern();
  30804             } else {
  30805                 runtime_src = elem_src;
  30806             }
  30807         }
  30808     }
  30809 
  30810     if (runtime_src) |rs| {
  30811         try sema.requireRuntimeBlock(block, inst_src, rs);
  30812         return block.addAggregateInit(dest_ty, element_refs);
  30813     }
  30814 
  30815     return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  30816         .ty = dest_ty.toIntern(),
  30817         .storage = .{ .elems = element_vals },
  30818     } })));
  30819 }
  30820 
  30821 /// If the lengths match, coerces element-wise.
  30822 fn coerceTupleToArray(
  30823     sema: *Sema,
  30824     block: *Block,
  30825     dest_ty: Type,
  30826     dest_ty_src: LazySrcLoc,
  30827     inst: Air.Inst.Ref,
  30828     inst_src: LazySrcLoc,
  30829 ) !Air.Inst.Ref {
  30830     const pt = sema.pt;
  30831     const zcu = pt.zcu;
  30832     const inst_ty = sema.typeOf(inst);
  30833     const inst_len = inst_ty.arrayLen(zcu);
  30834     const dest_len = dest_ty.arrayLen(zcu);
  30835 
  30836     if (dest_len != inst_len) {
  30837         const msg = msg: {
  30838             const msg = try sema.errMsg(inst_src, "expected type '{f}', found '{f}'", .{
  30839                 dest_ty.fmt(pt), inst_ty.fmt(pt),
  30840             });
  30841             errdefer msg.destroy(sema.gpa);
  30842             try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
  30843             try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
  30844             break :msg msg;
  30845         };
  30846         return sema.failWithOwnedErrorMsg(block, msg);
  30847     }
  30848 
  30849     const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len);
  30850     const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
  30851     const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
  30852     const dest_elem_ty = dest_ty.childType(zcu);
  30853 
  30854     var runtime_src: ?LazySrcLoc = null;
  30855     for (element_vals, element_refs, 0..) |*val, *ref, i_usize| {
  30856         const i: u32 = @intCast(i_usize);
  30857         if (i_usize == inst_len) {
  30858             const sentinel_val = dest_ty.sentinel(zcu).?;
  30859             val.* = sentinel_val.toIntern();
  30860             ref.* = Air.internedToRef(sentinel_val.toIntern());
  30861             break;
  30862         }
  30863         const elem_src = inst_src; // TODO better source location
  30864         const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i);
  30865         const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
  30866         ref.* = coerced;
  30867         if (runtime_src == null) {
  30868             if (try sema.resolveValue(coerced)) |elem_val| {
  30869                 val.* = elem_val.toIntern();
  30870             } else {
  30871                 runtime_src = elem_src;
  30872             }
  30873         }
  30874     }
  30875 
  30876     if (runtime_src) |rs| {
  30877         try sema.requireRuntimeBlock(block, inst_src, rs);
  30878         return block.addAggregateInit(dest_ty, element_refs);
  30879     }
  30880 
  30881     return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  30882         .ty = dest_ty.toIntern(),
  30883         .storage = .{ .elems = element_vals },
  30884     } })));
  30885 }
  30886 
  30887 /// If the lengths match, coerces element-wise.
  30888 fn coerceTupleToSlicePtrs(
  30889     sema: *Sema,
  30890     block: *Block,
  30891     slice_ty: Type,
  30892     slice_ty_src: LazySrcLoc,
  30893     ptr_tuple: Air.Inst.Ref,
  30894     tuple_src: LazySrcLoc,
  30895 ) !Air.Inst.Ref {
  30896     const pt = sema.pt;
  30897     const zcu = pt.zcu;
  30898     const tuple_ty = sema.typeOf(ptr_tuple).childType(zcu);
  30899     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  30900     const slice_info = slice_ty.ptrInfo(zcu);
  30901     const array_ty = try pt.arrayType(.{
  30902         .len = tuple_ty.structFieldCount(zcu),
  30903         .sentinel = slice_info.sentinel,
  30904         .child = slice_info.child,
  30905     });
  30906     const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
  30907     if (slice_info.flags.alignment != .none) {
  30908         return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  30909     }
  30910     const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst);
  30911     return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
  30912 }
  30913 
  30914 /// If the lengths match, coerces element-wise.
  30915 fn coerceTupleToArrayPtrs(
  30916     sema: *Sema,
  30917     block: *Block,
  30918     ptr_array_ty: Type,
  30919     array_ty_src: LazySrcLoc,
  30920     ptr_tuple: Air.Inst.Ref,
  30921     tuple_src: LazySrcLoc,
  30922 ) !Air.Inst.Ref {
  30923     const pt = sema.pt;
  30924     const zcu = pt.zcu;
  30925     const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
  30926     const ptr_info = ptr_array_ty.ptrInfo(zcu);
  30927     const array_ty: Type = .fromInterned(ptr_info.child);
  30928     const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src);
  30929     if (ptr_info.flags.alignment != .none) {
  30930         return sema.fail(block, array_ty_src, "TODO: override the alignment of the array decl we create here", .{});
  30931     }
  30932     const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst);
  30933     return ptr_array;
  30934 }
  30935 
  30936 fn coerceTupleToTuple(
  30937     sema: *Sema,
  30938     block: *Block,
  30939     tuple_ty: Type,
  30940     inst: Air.Inst.Ref,
  30941     inst_src: LazySrcLoc,
  30942 ) !Air.Inst.Ref {
  30943     const pt = sema.pt;
  30944     const zcu = pt.zcu;
  30945     const ip = &zcu.intern_pool;
  30946     const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30947         .tuple_type => |tuple_type| tuple_type.types.len,
  30948         else => unreachable,
  30949     };
  30950     const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count);
  30951     const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
  30952     @memset(field_refs, .none);
  30953 
  30954     const inst_ty = sema.typeOf(inst);
  30955     const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) {
  30956         .tuple_type => |tuple_type| tuple_type.types.len,
  30957         else => unreachable,
  30958     };
  30959     if (src_field_count > dest_field_count) return error.NotCoercible;
  30960 
  30961     var runtime_src: ?LazySrcLoc = null;
  30962     for (0..dest_field_count) |field_index_usize| {
  30963         const field_i: u32 = @intCast(field_index_usize);
  30964         const field_src = inst_src; // TODO better source location
  30965 
  30966         const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30967             .tuple_type => |tuple_type| tuple_type.types.get(ip)[field_index_usize],
  30968             .struct_type => ip.loadStructType(tuple_ty.toIntern()).field_types.get(ip)[field_index_usize],
  30969             else => unreachable,
  30970         };
  30971         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  30972             .tuple_type => |tuple_type| tuple_type.values.get(ip)[field_index_usize],
  30973             .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, field_index_usize),
  30974             else => unreachable,
  30975         };
  30976 
  30977         const field_index: u32 = @intCast(field_index_usize);
  30978 
  30979         const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
  30980         const coerced = try sema.coerce(block, .fromInterned(field_ty), elem_ref, field_src);
  30981         field_refs[field_index] = coerced;
  30982         if (default_val != .none) {
  30983             const init_val = (try sema.resolveValue(coerced)) orelse {
  30984                 return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field });
  30985             };
  30986 
  30987             if (!init_val.eql(Value.fromInterned(default_val), .fromInterned(field_ty), pt.zcu)) {
  30988                 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
  30989             }
  30990         }
  30991         if (runtime_src == null) {
  30992             if (try sema.resolveValue(coerced)) |field_val| {
  30993                 field_vals[field_index] = field_val.toIntern();
  30994             } else {
  30995                 runtime_src = field_src;
  30996             }
  30997         }
  30998     }
  30999 
  31000     // Populate default field values and report errors for missing fields.
  31001     var root_msg: ?*Zcu.ErrorMsg = null;
  31002     errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
  31003 
  31004     for (field_refs, 0..) |*field_ref, i_usize| {
  31005         const i: u32 = @intCast(i_usize);
  31006         if (field_ref.* != .none) continue;
  31007 
  31008         const default_val = switch (ip.indexToKey(tuple_ty.toIntern())) {
  31009             .tuple_type => |tuple_type| tuple_type.values.get(ip)[i],
  31010             .struct_type => ip.loadStructType(tuple_ty.toIntern()).fieldInit(ip, i),
  31011             else => unreachable,
  31012         };
  31013 
  31014         const field_src = inst_src; // TODO better source location
  31015         if (default_val == .none) {
  31016             const template = "missing tuple field: {d}";
  31017             if (root_msg) |msg| {
  31018                 try sema.errNote(field_src, msg, template, .{i});
  31019             } else {
  31020                 root_msg = try sema.errMsg(field_src, template, .{i});
  31021             }
  31022             continue;
  31023         }
  31024         if (runtime_src == null) {
  31025             field_vals[i] = default_val;
  31026         } else {
  31027             field_ref.* = Air.internedToRef(default_val);
  31028         }
  31029     }
  31030 
  31031     if (root_msg) |msg| {
  31032         try sema.addDeclaredHereNote(msg, tuple_ty);
  31033         root_msg = null;
  31034         return sema.failWithOwnedErrorMsg(block, msg);
  31035     }
  31036 
  31037     if (runtime_src) |rs| {
  31038         try sema.requireRuntimeBlock(block, inst_src, rs);
  31039         return block.addAggregateInit(tuple_ty, field_refs);
  31040     }
  31041 
  31042     return Air.internedToRef((try pt.intern(.{ .aggregate = .{
  31043         .ty = tuple_ty.toIntern(),
  31044         .storage = .{ .elems = field_vals },
  31045     } })));
  31046 }
  31047 
  31048 fn analyzeNavVal(
  31049     sema: *Sema,
  31050     block: *Block,
  31051     src: LazySrcLoc,
  31052     nav_index: InternPool.Nav.Index,
  31053 ) CompileError!Air.Inst.Ref {
  31054     const ref = try sema.analyzeNavRefInner(block, src, nav_index, false);
  31055     return sema.analyzeLoad(block, src, ref, src);
  31056 }
  31057 
  31058 fn addReferenceEntry(
  31059     sema: *Sema,
  31060     opt_block: ?*Block,
  31061     src: LazySrcLoc,
  31062     referenced_unit: AnalUnit,
  31063 ) !void {
  31064     const zcu = sema.pt.zcu;
  31065     if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
  31066     const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
  31067     if (gop.found_existing) return;
  31068     try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: {
  31069         const block = opt_block orelse break :inline_frame .none;
  31070         const inlining = block.inlining orelse break :inline_frame .none;
  31071         const frame = try inlining.refFrame(zcu);
  31072         break :inline_frame frame.toOptional();
  31073     });
  31074 }
  31075 
  31076 pub fn addTypeReferenceEntry(
  31077     sema: *Sema,
  31078     src: LazySrcLoc,
  31079     referenced_type: InternPool.Index,
  31080 ) !void {
  31081     const zcu = sema.pt.zcu;
  31082     if (!zcu.comp.incremental and zcu.comp.reference_trace == 0) return;
  31083     const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type);
  31084     if (gop.found_existing) return;
  31085     try zcu.addTypeReference(sema.owner, referenced_type, src);
  31086 }
  31087 
  31088 fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.MemoizedStateStage) SemaError!void {
  31089     const pt = sema.pt;
  31090 
  31091     const unit: AnalUnit = .wrap(.{ .memoized_state = stage });
  31092     try sema.addReferenceEntry(null, src, unit);
  31093     try sema.declareDependency(.{ .memoized_state = stage });
  31094 
  31095     if (pt.zcu.analysis_in_progress.contains(unit)) {
  31096         return sema.failWithOwnedErrorMsg(null, try sema.errMsg(src, "dependency loop detected", .{}));
  31097     }
  31098     try pt.ensureMemoizedStateUpToDate(stage);
  31099 }
  31100 
  31101 pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void {
  31102     const pt = sema.pt;
  31103     const zcu = pt.zcu;
  31104     const ip = &zcu.intern_pool;
  31105 
  31106     const nav = ip.getNav(nav_index);
  31107     if (nav.analysis == null) {
  31108         assert(nav.status == .fully_resolved);
  31109         return;
  31110     }
  31111 
  31112     try sema.declareDependency(switch (kind) {
  31113         .type => .{ .nav_ty = nav_index },
  31114         .fully => .{ .nav_val = nav_index },
  31115     });
  31116 
  31117     // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate`
  31118     // to make sure the value is up-to-date on incremental updates.
  31119 
  31120     const anal_unit: AnalUnit = .wrap(switch (kind) {
  31121         .type => .{ .nav_ty = nav_index },
  31122         .fully => .{ .nav_val = nav_index },
  31123     });
  31124     try sema.addReferenceEntry(block, src, anal_unit);
  31125 
  31126     if (zcu.analysis_in_progress.contains(anal_unit)) {
  31127         return sema.failWithOwnedErrorMsg(null, try sema.errMsg(.{
  31128             .base_node_inst = nav.analysis.?.zir_index,
  31129             .offset = LazySrcLoc.Offset.nodeOffset(.zero),
  31130         }, "dependency loop detected", .{}));
  31131     }
  31132 
  31133     switch (kind) {
  31134         .type => {
  31135             try zcu.ensureNavValAnalysisQueued(nav_index);
  31136             return pt.ensureNavTypeUpToDate(nav_index);
  31137         },
  31138         .fully => return pt.ensureNavValUpToDate(nav_index),
  31139     }
  31140 }
  31141 
  31142 fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
  31143     const pt = sema.pt;
  31144     const ptr_anyopaque_ty = try pt.singleConstPtrType(.anyopaque);
  31145     return Value.fromInterned(try pt.intern(.{ .opt = .{
  31146         .ty = (try pt.optionalType(ptr_anyopaque_ty.toIntern())).toIntern(),
  31147         .val = if (opt_val) |val| (try pt.getCoerced(
  31148             Value.fromInterned(try pt.refValue(val.toIntern())),
  31149             ptr_anyopaque_ty,
  31150         )).toIntern() else .none,
  31151     } }));
  31152 }
  31153 
  31154 fn analyzeNavRef(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!Air.Inst.Ref {
  31155     return sema.analyzeNavRefInner(block, src, nav_index, true);
  31156 }
  31157 
  31158 /// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed.
  31159 /// If this pointer will be used directly, `is_ref` must be `true`.
  31160 /// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`.
  31161 fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref {
  31162     const pt = sema.pt;
  31163     const zcu = pt.zcu;
  31164     const ip = &zcu.intern_pool;
  31165 
  31166     try sema.ensureNavResolved(block, src, orig_nav_index, if (is_ref) .type else .fully);
  31167 
  31168     const nav_index = nav: {
  31169         if (ip.getNav(orig_nav_index).isExternOrFn(ip)) {
  31170             // Getting a pointer to this `Nav` might mean we actually get a pointer to something else!
  31171             // We need to resolve the value to know for sure.
  31172             if (is_ref) try sema.ensureNavResolved(block, src, orig_nav_index, .fully);
  31173             switch (ip.indexToKey(ip.getNav(orig_nav_index).status.fully_resolved.val)) {
  31174                 .func => |f| break :nav f.owner_nav,
  31175                 .@"extern" => |e| break :nav e.owner_nav,
  31176                 else => {},
  31177             }
  31178         }
  31179         break :nav orig_nav_index;
  31180     };
  31181 
  31182     const nav_status = ip.getNav(nav_index).status;
  31183 
  31184     const is_runtime = switch (nav_status) {
  31185         .unresolved => unreachable,
  31186         // dllimports go straight to `fully_resolved`; the only option is threadlocal
  31187         .type_resolved => |r| r.is_threadlocal,
  31188         .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
  31189             .@"extern" => |e| e.is_threadlocal or e.is_dll_import or switch (e.relocation) {
  31190                 .any => false,
  31191                 .pcrel => true,
  31192             },
  31193             .variable => |v| v.is_threadlocal,
  31194             else => false,
  31195         },
  31196     };
  31197 
  31198     const ty, const alignment, const @"addrspace", const is_const = switch (nav_status) {
  31199         .unresolved => unreachable,
  31200         .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
  31201         .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const },
  31202     };
  31203     const ptr_ty = try pt.ptrTypeSema(.{
  31204         .child = ty,
  31205         .flags = .{
  31206             .alignment = alignment,
  31207             .is_const = is_const,
  31208             .address_space = @"addrspace",
  31209         },
  31210     });
  31211 
  31212     if (is_runtime) {
  31213         // This pointer is runtime-known; we need to emit an AIR instruction to create it.
  31214         return block.addInst(.{
  31215             .tag = .runtime_nav_ptr,
  31216             .data = .{ .ty_nav = .{
  31217                 .ty = ptr_ty.toIntern(),
  31218                 .nav = nav_index,
  31219             } },
  31220         });
  31221     }
  31222 
  31223     if (is_ref) {
  31224         try sema.maybeQueueFuncBodyAnalysis(block, src, nav_index);
  31225     }
  31226 
  31227     return Air.internedToRef((try pt.intern(.{ .ptr = .{
  31228         .ty = ptr_ty.toIntern(),
  31229         .base_addr = .{ .nav = nav_index },
  31230         .byte_offset = 0,
  31231     } })));
  31232 }
  31233 
  31234 fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void {
  31235     const pt = sema.pt;
  31236     const zcu = pt.zcu;
  31237     const ip = &zcu.intern_pool;
  31238 
  31239     // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function.
  31240     // If it is, we can resolve the *value*, and queue analysis as needed.
  31241 
  31242     try sema.ensureNavResolved(block, src, nav_index, .type);
  31243     const nav_ty: Type = .fromInterned(ip.getNav(nav_index).typeOf(ip));
  31244     if (nav_ty.zigTypeTag(zcu) != .@"fn") return;
  31245     if (!try nav_ty.fnHasRuntimeBitsSema(pt)) return;
  31246 
  31247     try sema.ensureNavResolved(block, src, nav_index, .fully);
  31248     const nav_val = zcu.navValue(nav_index);
  31249     if (!ip.isFuncBody(nav_val.toIntern())) return;
  31250 
  31251     try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = nav_val.toIntern() }));
  31252     try zcu.ensureFuncBodyAnalysisQueued(nav_val.toIntern());
  31253 }
  31254 
  31255 fn analyzeRef(
  31256     sema: *Sema,
  31257     block: *Block,
  31258     src: LazySrcLoc,
  31259     operand: Air.Inst.Ref,
  31260 ) CompileError!Air.Inst.Ref {
  31261     const pt = sema.pt;
  31262     const zcu = pt.zcu;
  31263     const operand_ty = sema.typeOf(operand);
  31264 
  31265     if (try sema.resolveValue(operand)) |val| {
  31266         switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  31267             .@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav),
  31268             .func => |f| return sema.analyzeNavRef(block, src, f.owner_nav),
  31269             else => return uavRef(sema, val.toIntern()),
  31270         }
  31271     }
  31272 
  31273     // No `requireRuntimeBlock`; it's okay to `ref` to a runtime value in a comptime context,
  31274     // it's just that we can only use the *type* of the result, since the value is runtime-known.
  31275 
  31276     const address_space = target_util.defaultAddressSpace(zcu.getTarget(), .local);
  31277     const ptr_type = try pt.ptrTypeSema(.{
  31278         .child = operand_ty.toIntern(),
  31279         .flags = .{
  31280             .is_const = true,
  31281             .address_space = address_space,
  31282         },
  31283     });
  31284     const mut_ptr_type = try pt.ptrTypeSema(.{
  31285         .child = operand_ty.toIntern(),
  31286         .flags = .{ .address_space = address_space },
  31287     });
  31288     const alloc = try block.addTy(.alloc, mut_ptr_type);
  31289 
  31290     // In a comptime context, the store would fail, since the operand is runtime-known. But that's
  31291     // okay; we don't actually need this store to succeed, since we're creating a runtime value in a
  31292     // comptime scope, so the value can never be used aside from to get its type.
  31293     if (!block.isComptime()) {
  31294         try sema.storePtr(block, src, alloc, operand);
  31295     }
  31296 
  31297     // Cast to the constant pointer type. We do this directly rather than going via `coerce` to
  31298     // avoid errors in the `block.isComptime()` case.
  31299     return block.addBitCast(ptr_type, alloc);
  31300 }
  31301 
  31302 fn analyzeLoad(
  31303     sema: *Sema,
  31304     block: *Block,
  31305     src: LazySrcLoc,
  31306     ptr: Air.Inst.Ref,
  31307     ptr_src: LazySrcLoc,
  31308 ) CompileError!Air.Inst.Ref {
  31309     const pt = sema.pt;
  31310     const zcu = pt.zcu;
  31311     const ptr_ty = sema.typeOf(ptr);
  31312     const elem_ty = switch (ptr_ty.zigTypeTag(zcu)) {
  31313         .pointer => ptr_ty.childType(zcu),
  31314         else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}),
  31315     };
  31316     if (elem_ty.zigTypeTag(zcu) == .@"opaque") {
  31317         return sema.fail(block, ptr_src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)});
  31318     }
  31319 
  31320     if (try sema.typeHasOnePossibleValue(elem_ty)) |opv| {
  31321         return Air.internedToRef(opv.toIntern());
  31322     }
  31323 
  31324     if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
  31325         if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |elem_val| {
  31326             return Air.internedToRef(elem_val.toIntern());
  31327         }
  31328     }
  31329 
  31330     if (ptr_ty.ptrInfo(zcu).flags.vector_index == .runtime) {
  31331         const ptr_inst = ptr.toIndex().?;
  31332         const air_tags = sema.air_instructions.items(.tag);
  31333         if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) {
  31334             const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl;
  31335             const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
  31336             return block.addBinOp(.ptr_elem_val, bin_op.lhs, bin_op.rhs);
  31337         }
  31338         return sema.fail(block, ptr_src, "unable to determine vector element index of type '{f}'", .{
  31339             ptr_ty.fmt(pt),
  31340         });
  31341     }
  31342 
  31343     return block.addTyOp(.load, elem_ty, ptr);
  31344 }
  31345 
  31346 fn analyzeSlicePtr(
  31347     sema: *Sema,
  31348     block: *Block,
  31349     slice_src: LazySrcLoc,
  31350     slice: Air.Inst.Ref,
  31351     slice_ty: Type,
  31352 ) CompileError!Air.Inst.Ref {
  31353     const pt = sema.pt;
  31354     const zcu = pt.zcu;
  31355     const result_ty = slice_ty.slicePtrFieldType(zcu);
  31356     if (try sema.resolveValue(slice)) |val| {
  31357         if (val.isUndef(zcu)) return pt.undefRef(result_ty);
  31358         return Air.internedToRef(val.slicePtr(zcu).toIntern());
  31359     }
  31360     try sema.requireRuntimeBlock(block, slice_src, null);
  31361     return block.addTyOp(.slice_ptr, result_ty, slice);
  31362 }
  31363 
  31364 fn analyzeOptionalSlicePtr(
  31365     sema: *Sema,
  31366     block: *Block,
  31367     opt_slice_src: LazySrcLoc,
  31368     opt_slice: Air.Inst.Ref,
  31369     opt_slice_ty: Type,
  31370 ) CompileError!Air.Inst.Ref {
  31371     const pt = sema.pt;
  31372     const zcu = pt.zcu;
  31373     const slice_ty = opt_slice_ty.optionalChild(zcu);
  31374     const result_ty = slice_ty.slicePtrFieldType(zcu);
  31375 
  31376     if (try sema.resolveValue(opt_slice)) |opt_val| {
  31377         if (opt_val.isUndef(zcu)) return pt.undefRef(result_ty);
  31378         const slice_ptr: InternPool.Index = if (opt_val.optionalValue(zcu)) |val|
  31379             val.slicePtr(zcu).toIntern()
  31380         else
  31381             .null_value;
  31382 
  31383         return Air.internedToRef(slice_ptr);
  31384     }
  31385 
  31386     try sema.requireRuntimeBlock(block, opt_slice_src, null);
  31387 
  31388     const slice = try block.addTyOp(.optional_payload, slice_ty, opt_slice);
  31389     return block.addTyOp(.slice_ptr, result_ty, slice);
  31390 }
  31391 
  31392 fn analyzeSliceLen(
  31393     sema: *Sema,
  31394     block: *Block,
  31395     src: LazySrcLoc,
  31396     slice_inst: Air.Inst.Ref,
  31397 ) CompileError!Air.Inst.Ref {
  31398     const pt = sema.pt;
  31399     const zcu = pt.zcu;
  31400     if (try sema.resolveValue(slice_inst)) |slice_val| {
  31401         if (slice_val.isUndef(zcu)) {
  31402             return .undef_usize;
  31403         }
  31404         return pt.intRef(.usize, try slice_val.sliceLen(pt));
  31405     }
  31406     try sema.requireRuntimeBlock(block, src, null);
  31407     return block.addTyOp(.slice_len, .usize, slice_inst);
  31408 }
  31409 
  31410 fn analyzeIsNull(
  31411     sema: *Sema,
  31412     block: *Block,
  31413     operand: Air.Inst.Ref,
  31414     invert_logic: bool,
  31415 ) CompileError!Air.Inst.Ref {
  31416     const pt = sema.pt;
  31417     const zcu = pt.zcu;
  31418     const result_ty: Type = .bool;
  31419     if (try sema.resolveValue(operand)) |opt_val| {
  31420         if (opt_val.isUndef(zcu)) {
  31421             return pt.undefRef(result_ty);
  31422         }
  31423         const is_null = opt_val.isNull(zcu);
  31424         const bool_value = if (invert_logic) !is_null else is_null;
  31425         return if (bool_value) .bool_true else .bool_false;
  31426     }
  31427 
  31428     if (sema.typeOf(operand).isNullFromType(zcu)) |is_null| {
  31429         const result = is_null != invert_logic;
  31430         return if (result) .bool_true else .bool_false;
  31431     }
  31432     const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
  31433     return block.addUnOp(air_tag, operand);
  31434 }
  31435 
  31436 fn analyzePtrIsNonErrComptimeOnly(
  31437     sema: *Sema,
  31438     block: *Block,
  31439     src: LazySrcLoc,
  31440     operand: Air.Inst.Ref,
  31441 ) CompileError!Air.Inst.Ref {
  31442     const pt = sema.pt;
  31443     const zcu = pt.zcu;
  31444     const ptr_ty = sema.typeOf(operand);
  31445     assert(ptr_ty.zigTypeTag(zcu) == .pointer);
  31446     const child_ty = ptr_ty.childType(zcu);
  31447 
  31448     const child_tag = child_ty.zigTypeTag(zcu);
  31449     if (child_tag != .error_set and child_tag != .error_union) return .bool_true;
  31450     if (child_tag == .error_set) return .bool_false;
  31451     assert(child_tag == .error_union);
  31452 
  31453     _ = block;
  31454     _ = src;
  31455 
  31456     return .none;
  31457 }
  31458 
  31459 fn analyzeIsNonErrComptimeOnly(
  31460     sema: *Sema,
  31461     block: *Block,
  31462     src: LazySrcLoc,
  31463     operand: Air.Inst.Ref,
  31464 ) CompileError!Air.Inst.Ref {
  31465     const pt = sema.pt;
  31466     const zcu = pt.zcu;
  31467     const ip = &zcu.intern_pool;
  31468     const operand_ty = sema.typeOf(operand);
  31469     const ot = operand_ty.zigTypeTag(zcu);
  31470     if (ot != .error_set and ot != .error_union) return .bool_true;
  31471     if (ot == .error_set) return .bool_false;
  31472     assert(ot == .error_union);
  31473 
  31474     const payload_ty = operand_ty.errorUnionPayload(zcu);
  31475     if (payload_ty.zigTypeTag(zcu) == .noreturn) {
  31476         return .bool_false;
  31477     }
  31478 
  31479     if (operand.toIndex()) |operand_inst| {
  31480         switch (sema.air_instructions.items(.tag)[@intFromEnum(operand_inst)]) {
  31481             .wrap_errunion_payload => return .bool_true,
  31482             .wrap_errunion_err => return .bool_false,
  31483             else => {},
  31484         }
  31485     } else if (operand == .undef) {
  31486         return .undef_bool;
  31487     } else if (@intFromEnum(operand) < InternPool.static_len) {
  31488         // None of the ref tags can be errors.
  31489         return .bool_true;
  31490     }
  31491 
  31492     const maybe_operand_val = try sema.resolveValue(operand);
  31493 
  31494     // exception if the error union error set is known to be empty,
  31495     // we allow the comparison but always make it comptime-known.
  31496     const set_ty = ip.errorUnionSet(operand_ty.toIntern());
  31497     switch (set_ty) {
  31498         .anyerror_type => {},
  31499         .adhoc_inferred_error_set_type => if (sema.fn_ret_ty_ies) |ies| blk: {
  31500             // If the error set is empty, we must return a comptime true or false.
  31501             // However we want to avoid unnecessarily resolving an inferred error set
  31502             // in case it is already non-empty.
  31503             switch (ies.resolved) {
  31504                 .anyerror_type => break :blk,
  31505                 .none => {},
  31506                 else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
  31507             }
  31508 
  31509             if (maybe_operand_val != null) break :blk;
  31510 
  31511             // Try to avoid resolving inferred error set if possible.
  31512             if (ies.errors.count() != 0) return .none;
  31513             switch (ies.resolved) {
  31514                 .anyerror_type => return .none,
  31515                 .none => {},
  31516                 else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
  31517                     0 => return .bool_true,
  31518                     else => return .none,
  31519                 },
  31520             }
  31521             // We do not have a comptime answer because this inferred error
  31522             // set is not resolved, and an instruction later in this function
  31523             // body may or may not cause an error to be added to this set.
  31524             return .none;
  31525         },
  31526         else => switch (ip.indexToKey(set_ty)) {
  31527             .error_set_type => |error_set_type| {
  31528                 if (error_set_type.names.len == 0) return .bool_true;
  31529             },
  31530             .inferred_error_set_type => |func_index| blk: {
  31531                 // If the error set is empty, we must return a comptime true or false.
  31532                 // However we want to avoid unnecessarily resolving an inferred error set
  31533                 // in case it is already non-empty.
  31534                 try zcu.maybeUnresolveIes(func_index);
  31535                 switch (ip.funcIesResolvedUnordered(func_index)) {
  31536                     .anyerror_type => break :blk,
  31537                     .none => {},
  31538                     else => |i| if (ip.indexToKey(i).error_set_type.names.len != 0) break :blk,
  31539                 }
  31540                 if (maybe_operand_val != null) break :blk;
  31541                 if (sema.fn_ret_ty_ies) |ies| {
  31542                     if (ies.func == func_index) {
  31543                         // Try to avoid resolving inferred error set if possible.
  31544                         if (ies.errors.count() != 0) return .none;
  31545                         switch (ies.resolved) {
  31546                             .anyerror_type => return .none,
  31547                             .none => {},
  31548                             else => switch (ip.indexToKey(ies.resolved).error_set_type.names.len) {
  31549                                 0 => return .bool_true,
  31550                                 else => return .none,
  31551                             },
  31552                         }
  31553                         // We do not have a comptime answer because this inferred error
  31554                         // set is not resolved, and an instruction later in this function
  31555                         // body may or may not cause an error to be added to this set.
  31556                         return .none;
  31557                     }
  31558                 }
  31559                 const resolved_ty = try sema.resolveInferredErrorSet(block, src, set_ty);
  31560                 if (resolved_ty == .anyerror_type)
  31561                     break :blk;
  31562                 if (ip.indexToKey(resolved_ty).error_set_type.names.len == 0)
  31563                     return .bool_true;
  31564             },
  31565             else => unreachable,
  31566         },
  31567     }
  31568 
  31569     if (maybe_operand_val) |err_union| {
  31570         return if (err_union.isUndef(zcu)) .undef_bool else if (err_union.getErrorName(zcu) == .none) .bool_true else .bool_false;
  31571     }
  31572     return .none;
  31573 }
  31574 
  31575 fn analyzeIsNonErr(
  31576     sema: *Sema,
  31577     block: *Block,
  31578     src: LazySrcLoc,
  31579     operand: Air.Inst.Ref,
  31580 ) CompileError!Air.Inst.Ref {
  31581     const result = try sema.analyzeIsNonErrComptimeOnly(block, src, operand);
  31582     if (result == .none) {
  31583         try sema.requireRuntimeBlock(block, src, null);
  31584         return block.addUnOp(.is_non_err, operand);
  31585     } else {
  31586         return result;
  31587     }
  31588 }
  31589 
  31590 fn analyzePtrIsNonErr(
  31591     sema: *Sema,
  31592     block: *Block,
  31593     src: LazySrcLoc,
  31594     operand: Air.Inst.Ref,
  31595 ) CompileError!Air.Inst.Ref {
  31596     const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand);
  31597     if (result == .none) {
  31598         try sema.requireRuntimeBlock(block, src, null);
  31599         return block.addUnOp(.is_non_err_ptr, operand);
  31600     } else {
  31601         return result;
  31602     }
  31603 }
  31604 
  31605 fn analyzeSlice(
  31606     sema: *Sema,
  31607     block: *Block,
  31608     src: LazySrcLoc,
  31609     ptr_ptr: Air.Inst.Ref,
  31610     uncasted_start: Air.Inst.Ref,
  31611     uncasted_end_opt: Air.Inst.Ref,
  31612     sentinel_opt: Air.Inst.Ref,
  31613     sentinel_src: LazySrcLoc,
  31614     ptr_src: LazySrcLoc,
  31615     start_src: LazySrcLoc,
  31616     end_src: LazySrcLoc,
  31617     by_length: bool,
  31618 ) CompileError!Air.Inst.Ref {
  31619     const pt = sema.pt;
  31620     const zcu = pt.zcu;
  31621     // Slice expressions can operate on a variable whose type is an array. This requires
  31622     // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
  31623     const ptr_ptr_ty = sema.typeOf(ptr_ptr);
  31624     const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(zcu)) {
  31625         .pointer => ptr_ptr_ty.childType(zcu),
  31626         else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ptr_ty.fmt(pt)}),
  31627     };
  31628 
  31629     var array_ty = ptr_ptr_child_ty;
  31630     var slice_ty = ptr_ptr_ty;
  31631     var ptr_or_slice = ptr_ptr;
  31632     var elem_ty: Type = undefined;
  31633     var ptr_sentinel: ?Value = null;
  31634     switch (ptr_ptr_child_ty.zigTypeTag(zcu)) {
  31635         .array => {
  31636             ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu);
  31637             elem_ty = ptr_ptr_child_ty.childType(zcu);
  31638         },
  31639         .pointer => switch (ptr_ptr_child_ty.ptrSize(zcu)) {
  31640             .one => {
  31641                 const double_child_ty = ptr_ptr_child_ty.childType(zcu);
  31642                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  31643                 if (double_child_ty.zigTypeTag(zcu) == .array) {
  31644                     ptr_sentinel = double_child_ty.sentinel(zcu);
  31645                     slice_ty = ptr_ptr_child_ty;
  31646                     array_ty = double_child_ty;
  31647                     elem_ty = double_child_ty.childType(zcu);
  31648                 } else {
  31649                     if (uncasted_end_opt == .none) {
  31650                         return sema.fail(block, src, "slice of single-item pointer must be bounded", .{});
  31651                     }
  31652                     const start_value = try sema.resolveConstDefinedValue(
  31653                         block,
  31654                         start_src,
  31655                         uncasted_start,
  31656                         .{ .simple = .slice_single_item_ptr_bounds },
  31657                     );
  31658 
  31659                     const end_value = try sema.resolveConstDefinedValue(
  31660                         block,
  31661                         end_src,
  31662                         uncasted_end_opt,
  31663                         .{ .simple = .slice_single_item_ptr_bounds },
  31664                     );
  31665 
  31666                     const bounds_error_message = "slice of single-item pointer must have bounds [0..0], [0..1], or [1..1]";
  31667                     if (try sema.compareScalar(start_value, .neq, end_value, .comptime_int)) {
  31668                         if (try sema.compareScalar(start_value, .neq, Value.zero_comptime_int, .comptime_int)) {
  31669                             const msg = msg: {
  31670                                 const msg = try sema.errMsg(start_src, bounds_error_message, .{});
  31671                                 errdefer msg.destroy(sema.gpa);
  31672                                 try sema.errNote(
  31673                                     start_src,
  31674                                     msg,
  31675                                     "expected '{f}', found '{f}'",
  31676                                     .{
  31677                                         Value.zero_comptime_int.fmtValueSema(pt, sema),
  31678                                         start_value.fmtValueSema(pt, sema),
  31679                                     },
  31680                                 );
  31681                                 break :msg msg;
  31682                             };
  31683                             return sema.failWithOwnedErrorMsg(block, msg);
  31684                         } else if (try sema.compareScalar(end_value, .neq, Value.one_comptime_int, .comptime_int)) {
  31685                             const msg = msg: {
  31686                                 const msg = try sema.errMsg(end_src, bounds_error_message, .{});
  31687                                 errdefer msg.destroy(sema.gpa);
  31688                                 try sema.errNote(
  31689                                     end_src,
  31690                                     msg,
  31691                                     "expected '{f}', found '{f}'",
  31692                                     .{
  31693                                         Value.one_comptime_int.fmtValueSema(pt, sema),
  31694                                         end_value.fmtValueSema(pt, sema),
  31695                                     },
  31696                                 );
  31697                                 break :msg msg;
  31698                             };
  31699                             return sema.failWithOwnedErrorMsg(block, msg);
  31700                         }
  31701                     } else {
  31702                         if (try sema.compareScalar(end_value, .gt, Value.one_comptime_int, .comptime_int)) {
  31703                             return sema.fail(
  31704                                 block,
  31705                                 end_src,
  31706                                 "end index {f} out of bounds for slice of single-item pointer",
  31707                                 .{end_value.fmtValueSema(pt, sema)},
  31708                             );
  31709                         }
  31710                     }
  31711 
  31712                     array_ty = try pt.arrayType(.{
  31713                         .len = 1,
  31714                         .child = double_child_ty.toIntern(),
  31715                     });
  31716                     const ptr_info = ptr_ptr_child_ty.ptrInfo(zcu);
  31717                     slice_ty = try pt.ptrType(.{
  31718                         .child = array_ty.toIntern(),
  31719                         .flags = .{
  31720                             .alignment = ptr_info.flags.alignment,
  31721                             .is_const = ptr_info.flags.is_const,
  31722                             .is_allowzero = ptr_info.flags.is_allowzero,
  31723                             .is_volatile = ptr_info.flags.is_volatile,
  31724                             .address_space = ptr_info.flags.address_space,
  31725                         },
  31726                     });
  31727                     elem_ty = double_child_ty;
  31728                 }
  31729             },
  31730             .many, .c => {
  31731                 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu);
  31732                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  31733                 slice_ty = ptr_ptr_child_ty;
  31734                 array_ty = ptr_ptr_child_ty;
  31735                 elem_ty = ptr_ptr_child_ty.childType(zcu);
  31736 
  31737                 if (ptr_ptr_child_ty.ptrSize(zcu) == .c) {
  31738                     if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| {
  31739                         if (ptr_val.isNull(zcu)) {
  31740                             return sema.fail(block, src, "slice of null pointer", .{});
  31741                         }
  31742                     }
  31743                 }
  31744             },
  31745             .slice => {
  31746                 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu);
  31747                 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
  31748                 slice_ty = ptr_ptr_child_ty;
  31749                 array_ty = ptr_ptr_child_ty;
  31750                 elem_ty = ptr_ptr_child_ty.childType(zcu);
  31751             },
  31752         },
  31753         else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}),
  31754     }
  31755 
  31756     const ptr = if (slice_ty.isSlice(zcu))
  31757         try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty)
  31758     else if (array_ty.zigTypeTag(zcu) == .array) ptr: {
  31759         var manyptr_ty_key = zcu.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type;
  31760         assert(manyptr_ty_key.child == array_ty.toIntern());
  31761         assert(manyptr_ty_key.flags.size == .one);
  31762         manyptr_ty_key.child = elem_ty.toIntern();
  31763         manyptr_ty_key.flags.size = .many;
  31764         break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrTypeSema(manyptr_ty_key), ptr_or_slice, ptr_src);
  31765     } else ptr_or_slice;
  31766 
  31767     const start = try sema.coerce(block, .usize, uncasted_start, start_src);
  31768     const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
  31769     const new_ptr_ty = sema.typeOf(new_ptr);
  31770 
  31771     // true if and only if the end index of the slice, implicitly or explicitly, equals
  31772     // the length of the underlying object being sliced. we might learn the length of the
  31773     // underlying object because it is an array (which has the length in the type), or
  31774     // we might learn of the length because it is a comptime-known slice value.
  31775     var end_is_len = uncasted_end_opt == .none;
  31776     const end = e: {
  31777         if (array_ty.zigTypeTag(zcu) == .array) {
  31778             const len_val = try pt.intValue(.usize, array_ty.arrayLen(zcu));
  31779 
  31780             if (!end_is_len) {
  31781                 const end = if (by_length) end: {
  31782                     const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31783                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  31784                     break :end try sema.coerce(block, .usize, uncasted_end, end_src);
  31785                 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31786                 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  31787                     const len_s_val = try pt.intValue(
  31788                         .usize,
  31789                         array_ty.arrayLenIncludingSentinel(zcu),
  31790                     );
  31791                     if (!(try sema.compareAll(end_val, .lte, len_s_val, .usize))) {
  31792                         const sentinel_label: []const u8 = if (array_ty.sentinel(zcu) != null)
  31793                             " +1 (sentinel)"
  31794                         else
  31795                             "";
  31796 
  31797                         return sema.fail(
  31798                             block,
  31799                             end_src,
  31800                             "end index {f} out of bounds for array of length {f}{s}",
  31801                             .{
  31802                                 end_val.fmtValueSema(pt, sema),
  31803                                 len_val.fmtValueSema(pt, sema),
  31804                                 sentinel_label,
  31805                             },
  31806                         );
  31807                     }
  31808 
  31809                     // end_is_len is only true if we are NOT using the sentinel
  31810                     // length. For sentinel-length, we don't want the type to
  31811                     // contain the sentinel.
  31812                     if (end_val.eql(len_val, .usize, zcu)) {
  31813                         end_is_len = true;
  31814                     }
  31815                 }
  31816                 break :e end;
  31817             }
  31818 
  31819             break :e Air.internedToRef(len_val.toIntern());
  31820         } else if (slice_ty.isSlice(zcu)) {
  31821             if (!end_is_len) {
  31822                 const end = if (by_length) end: {
  31823                     const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31824                     const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  31825                     break :end try sema.coerce(block, .usize, uncasted_end, end_src);
  31826                 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31827                 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  31828                     if (try sema.resolveValue(ptr_or_slice)) |slice_val| {
  31829                         if (slice_val.isUndef(zcu)) {
  31830                             return sema.fail(block, src, "slice of undefined", .{});
  31831                         }
  31832                         const has_sentinel = slice_ty.sentinel(zcu) != null;
  31833                         const slice_len = try slice_val.sliceLen(pt);
  31834                         const len_plus_sent = slice_len + @intFromBool(has_sentinel);
  31835                         const slice_len_val_with_sentinel = try pt.intValue(.usize, len_plus_sent);
  31836                         if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, .usize))) {
  31837                             const sentinel_label: []const u8 = if (has_sentinel)
  31838                                 " +1 (sentinel)"
  31839                             else
  31840                                 "";
  31841 
  31842                             return sema.fail(
  31843                                 block,
  31844                                 end_src,
  31845                                 "end index {f} out of bounds for slice of length {d}{s}",
  31846                                 .{
  31847                                     end_val.fmtValueSema(pt, sema),
  31848                                     try slice_val.sliceLen(pt),
  31849                                     sentinel_label,
  31850                                 },
  31851                             );
  31852                         }
  31853 
  31854                         // If the slice has a sentinel, we consider end_is_len
  31855                         // is only true if it equals the length WITHOUT the
  31856                         // sentinel, so we don't add a sentinel type.
  31857                         const slice_len_val = try pt.intValue(.usize, slice_len);
  31858                         if (end_val.eql(slice_len_val, .usize, zcu)) {
  31859                             end_is_len = true;
  31860                         }
  31861                     }
  31862                 }
  31863                 break :e end;
  31864             }
  31865             break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
  31866         }
  31867         if (!end_is_len) {
  31868             if (by_length) {
  31869                 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31870                 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
  31871                 break :e try sema.coerce(block, .usize, uncasted_end, end_src);
  31872             } else break :e try sema.coerce(block, .usize, uncasted_end_opt, end_src);
  31873         }
  31874 
  31875         // when slicing a many-item pointer, if a sentinel `S` is provided as in `ptr[a.. :S]`, it
  31876         // must match the sentinel of `@TypeOf(ptr)`.
  31877         sentinel_check: {
  31878             if (sentinel_opt == .none) break :sentinel_check;
  31879             const provided = provided: {
  31880                 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
  31881                 try checkSentinelType(sema, block, sentinel_src, elem_ty);
  31882                 break :provided try sema.resolveConstDefinedValue(
  31883                     block,
  31884                     sentinel_src,
  31885                     casted,
  31886                     .{ .simple = .slice_sentinel },
  31887                 );
  31888             };
  31889 
  31890             if (ptr_sentinel) |current| {
  31891                 if (provided.toIntern() == current.toIntern()) break :sentinel_check;
  31892             }
  31893 
  31894             return sema.failWithOwnedErrorMsg(block, msg: {
  31895                 const msg = try sema.errMsg(sentinel_src, "sentinel-terminated slicing of many-item pointer must match existing sentinel", .{});
  31896                 errdefer msg.destroy(sema.gpa);
  31897                 if (ptr_sentinel) |current| {
  31898                     try sema.errNote(sentinel_src, msg, "expected sentinel '{f}', found '{f}'", .{ current.fmtValue(pt), provided.fmtValue(pt) });
  31899                 } else {
  31900                     try sema.errNote(ptr_src, msg, "type '{f}' does not have a sentinel", .{slice_ty.fmt(pt)});
  31901                 }
  31902                 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{});
  31903                 break :msg msg;
  31904             });
  31905         }
  31906         return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, ptr_src, start_src);
  31907     };
  31908 
  31909     const sentinel = s: {
  31910         if (sentinel_opt != .none) {
  31911             const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
  31912             try checkSentinelType(sema, block, sentinel_src, elem_ty);
  31913             break :s try sema.resolveConstDefinedValue(block, sentinel_src, casted, .{ .simple = .slice_sentinel });
  31914         }
  31915         // If we are slicing to the end of something that is sentinel-terminated
  31916         // then the resulting slice type is also sentinel-terminated.
  31917         if (end_is_len) {
  31918             if (ptr_sentinel) |sent| {
  31919                 break :s sent;
  31920             }
  31921         }
  31922         break :s null;
  31923     };
  31924     const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
  31925 
  31926     var checked_start_lte_end = by_length;
  31927     var runtime_src: ?LazySrcLoc = null;
  31928 
  31929     // requirement: start <= end
  31930     if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
  31931         if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
  31932             if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, .usize))) {
  31933                 return sema.fail(
  31934                     block,
  31935                     start_src,
  31936                     "start index {f} is larger than end index {f}",
  31937                     .{
  31938                         start_val.fmtValueSema(pt, sema),
  31939                         end_val.fmtValueSema(pt, sema),
  31940                     },
  31941                 );
  31942             }
  31943             checked_start_lte_end = true;
  31944             if (try sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: {
  31945                 const expected_sentinel = sentinel orelse break :sentinel_check;
  31946                 const start_int = start_val.toUnsignedInt(zcu);
  31947                 const end_int = end_val.toUnsignedInt(zcu);
  31948                 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
  31949 
  31950                 const many_ptr_ty = try pt.manyConstPtrType(elem_ty);
  31951                 const many_ptr_val = try pt.getCoerced(ptr_val, many_ptr_ty);
  31952                 const elem_ptr = try many_ptr_val.ptrElem(sentinel_index, pt);
  31953                 const res = try sema.pointerDerefExtra(block, src, elem_ptr);
  31954                 const actual_sentinel = switch (res) {
  31955                     .runtime_load => break :sentinel_check,
  31956                     .val => |v| v,
  31957                     .needed_well_defined => |ty| return sema.fail(
  31958                         block,
  31959                         src,
  31960                         "comptime dereference requires '{f}' to have a well-defined layout",
  31961                         .{ty.fmt(pt)},
  31962                     ),
  31963                     .out_of_bounds => |ty| return sema.fail(
  31964                         block,
  31965                         end_src,
  31966                         "slice end index {d} exceeds bounds of containing decl of type '{f}'",
  31967                         .{ end_int, ty.fmt(pt) },
  31968                     ),
  31969                 };
  31970 
  31971                 if (!actual_sentinel.eql(expected_sentinel, elem_ty, zcu)) {
  31972                     const msg = msg: {
  31973                         const msg = try sema.errMsg(src, "value in memory does not match slice sentinel", .{});
  31974                         errdefer msg.destroy(sema.gpa);
  31975                         try sema.errNote(src, msg, "expected '{f}', found '{f}'", .{
  31976                             expected_sentinel.fmtValueSema(pt, sema),
  31977                             actual_sentinel.fmtValueSema(pt, sema),
  31978                         });
  31979 
  31980                         break :msg msg;
  31981                     };
  31982                     return sema.failWithOwnedErrorMsg(block, msg);
  31983                 }
  31984             } else {
  31985                 runtime_src = ptr_src;
  31986             }
  31987         } else {
  31988             runtime_src = start_src;
  31989         }
  31990     } else {
  31991         runtime_src = end_src;
  31992     }
  31993 
  31994     if (!checked_start_lte_end and block.wantSafety() and !block.isComptime()) {
  31995         // requirement: start <= end
  31996         assert(!block.isComptime());
  31997         try sema.requireRuntimeBlock(block, src, runtime_src.?);
  31998         const ok = try block.addBinOp(.cmp_lte, start, end);
  31999         try sema.addSafetyCheckCall(block, src, ok, .@"panic.startGreaterThanEnd", &.{ start, end });
  32000     }
  32001     const new_len = if (by_length)
  32002         try sema.coerce(block, .usize, uncasted_end_opt, end_src)
  32003     else
  32004         try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
  32005     const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
  32006 
  32007     const new_ptr_ty_info = new_ptr_ty.ptrInfo(zcu);
  32008     const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(zcu) != .c;
  32009 
  32010     if (opt_new_len_val) |new_len_val| {
  32011         const new_len_int = try new_len_val.toUnsignedIntSema(pt);
  32012 
  32013         const return_ty = try pt.ptrTypeSema(.{
  32014             .child = (try pt.arrayType(.{
  32015                 .len = new_len_int,
  32016                 .sentinel = if (sentinel) |s| s.toIntern() else .none,
  32017                 .child = elem_ty.toIntern(),
  32018             })).toIntern(),
  32019             .flags = .{
  32020                 .alignment = new_ptr_ty_info.flags.alignment,
  32021                 .is_const = new_ptr_ty_info.flags.is_const,
  32022                 .is_allowzero = new_allowzero,
  32023                 .is_volatile = new_ptr_ty_info.flags.is_volatile,
  32024                 .address_space = new_ptr_ty_info.flags.address_space,
  32025             },
  32026         });
  32027 
  32028         const opt_new_ptr_val = try sema.resolveValue(new_ptr);
  32029         const new_ptr_val = opt_new_ptr_val orelse {
  32030             const result = try block.addBitCast(return_ty, new_ptr);
  32031             if (block.wantSafety()) {
  32032                 // requirement: slicing C ptr is non-null
  32033                 if (ptr_ptr_child_ty.isCPtr(zcu)) {
  32034                     const is_non_null = try sema.analyzeIsNull(block, ptr, true);
  32035                     try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
  32036                 }
  32037 
  32038                 bounds_check: {
  32039                     const actual_len = if (array_ty.zigTypeTag(zcu) == .array)
  32040                         try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu))
  32041                     else if (slice_ty.isSlice(zcu)) l: {
  32042                         const slice_len = try sema.analyzeSliceLen(block, src, ptr_or_slice);
  32043                         break :l if (slice_ty.sentinel(zcu) == null)
  32044                             slice_len
  32045                         else
  32046                             try sema.analyzeArithmetic(block, .add, slice_len, .one, src, end_src, end_src, true);
  32047                     } else break :bounds_check;
  32048 
  32049                     const actual_end = if (slice_sentinel != null)
  32050                         try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  32051                     else
  32052                         end;
  32053 
  32054                     try sema.addSafetyCheckIndexOob(block, src, actual_end, actual_len, .cmp_lte);
  32055                 }
  32056 
  32057                 // requirement: result[new_len] == slice_sentinel
  32058                 try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
  32059             }
  32060             return result;
  32061         };
  32062 
  32063         if (!new_ptr_val.isUndef(zcu)) {
  32064             return Air.internedToRef((try pt.getCoerced(new_ptr_val, return_ty)).toIntern());
  32065         }
  32066 
  32067         // Special case: @as([]i32, undefined)[x..x]
  32068         if (new_len_int == 0) {
  32069             return pt.undefRef(return_ty);
  32070         }
  32071 
  32072         return sema.fail(block, src, "non-zero length slice of undefined pointer", .{});
  32073     }
  32074 
  32075     const return_ty = try pt.ptrTypeSema(.{
  32076         .child = elem_ty.toIntern(),
  32077         .sentinel = if (sentinel) |s| s.toIntern() else .none,
  32078         .flags = .{
  32079             .size = .slice,
  32080             .alignment = new_ptr_ty_info.flags.alignment,
  32081             .is_const = new_ptr_ty_info.flags.is_const,
  32082             .is_volatile = new_ptr_ty_info.flags.is_volatile,
  32083             .is_allowzero = new_allowzero,
  32084             .address_space = new_ptr_ty_info.flags.address_space,
  32085         },
  32086     });
  32087 
  32088     try sema.requireRuntimeBlock(block, src, runtime_src.?);
  32089     if (block.wantSafety()) {
  32090         // requirement: slicing C ptr is non-null
  32091         if (ptr_ptr_child_ty.isCPtr(zcu)) {
  32092             const is_non_null = try sema.analyzeIsNull(block, ptr, true);
  32093             try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
  32094         }
  32095 
  32096         // requirement: end <= len
  32097         const opt_len_inst = if (array_ty.zigTypeTag(zcu) == .array)
  32098             try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu))
  32099         else if (slice_ty.isSlice(zcu)) blk: {
  32100             if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
  32101                 // we don't need to add one for sentinels because the
  32102                 // underlying value data includes the sentinel
  32103                 break :blk try pt.intRef(.usize, try slice_val.sliceLen(pt));
  32104             }
  32105 
  32106             const slice_len_inst = try block.addTyOp(.slice_len, .usize, ptr_or_slice);
  32107             if (slice_ty.sentinel(zcu) == null) break :blk slice_len_inst;
  32108 
  32109             // we have to add one because slice lengths don't include the sentinel
  32110             break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
  32111         } else null;
  32112         if (opt_len_inst) |len_inst| {
  32113             const actual_end = if (slice_sentinel != null)
  32114                 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
  32115             else
  32116                 end;
  32117             try sema.addSafetyCheckIndexOob(block, src, actual_end, len_inst, .cmp_lte);
  32118         }
  32119 
  32120         // requirement: start <= end
  32121         try sema.addSafetyCheckIndexOob(block, src, start, end, .cmp_lte);
  32122     }
  32123     const result = try block.addInst(.{
  32124         .tag = .slice,
  32125         .data = .{ .ty_pl = .{
  32126             .ty = Air.internedToRef(return_ty.toIntern()),
  32127             .payload = try sema.addExtra(Air.Bin{
  32128                 .lhs = new_ptr,
  32129                 .rhs = new_len,
  32130             }),
  32131         } },
  32132     });
  32133     if (block.wantSafety()) {
  32134         // requirement: result[new_len] == slice_sentinel
  32135         try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
  32136     }
  32137     return result;
  32138 }
  32139 
  32140 /// Asserts that lhs and rhs types are both numeric.
  32141 fn cmpNumeric(
  32142     sema: *Sema,
  32143     block: *Block,
  32144     src: LazySrcLoc,
  32145     uncasted_lhs: Air.Inst.Ref,
  32146     uncasted_rhs: Air.Inst.Ref,
  32147     op: std.math.CompareOperator,
  32148     lhs_src: LazySrcLoc,
  32149     rhs_src: LazySrcLoc,
  32150 ) CompileError!Air.Inst.Ref {
  32151     const pt = sema.pt;
  32152     const zcu = pt.zcu;
  32153     const lhs_ty = sema.typeOf(uncasted_lhs);
  32154     const rhs_ty = sema.typeOf(uncasted_rhs);
  32155 
  32156     assert(lhs_ty.isNumeric(zcu));
  32157     assert(rhs_ty.isNumeric(zcu));
  32158 
  32159     const lhs_ty_tag = lhs_ty.zigTypeTag(zcu);
  32160     const rhs_ty_tag = rhs_ty.zigTypeTag(zcu);
  32161     const target = zcu.getTarget();
  32162 
  32163     // One exception to heterogeneous comparison: comptime_float needs to
  32164     // coerce to fixed-width float.
  32165 
  32166     const lhs = if (lhs_ty_tag == .comptime_float and rhs_ty_tag == .float)
  32167         try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src)
  32168     else
  32169         uncasted_lhs;
  32170 
  32171     const rhs = if (lhs_ty_tag == .float and rhs_ty_tag == .comptime_float)
  32172         try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src)
  32173     else
  32174         uncasted_rhs;
  32175 
  32176     const maybe_lhs_val = try sema.resolveValue(lhs);
  32177     const maybe_rhs_val = try sema.resolveValue(rhs);
  32178 
  32179     // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS value.
  32180     if (maybe_lhs_val) |lhs_val| {
  32181         // Result based on comparison exceeding type bounds
  32182         if (!lhs_val.isUndef(zcu) and (lhs_ty_tag == .int or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) {
  32183             if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| {
  32184                 return if (res) .bool_true else .bool_false;
  32185             }
  32186         }
  32187         // Result based on NaN comparison
  32188         if (lhs_val.isNan(zcu)) {
  32189             return if (op == .neq) .bool_true else .bool_false;
  32190         }
  32191         // Result based on inf comparison to int
  32192         if (lhs_val.isInf(zcu) and rhs_ty_tag == .int) return switch (op) {
  32193             .neq => .bool_true,
  32194             .eq => .bool_false,
  32195             .gt, .gte => if (lhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
  32196             .lt, .lte => if (lhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
  32197         };
  32198     }
  32199 
  32200     // If the RHS is const, check if there is a guaranteed result which does not depend on ths LHS value.
  32201     if (maybe_rhs_val) |rhs_val| {
  32202         // Result based on comparison exceeding type bounds
  32203         if (!rhs_val.isUndef(zcu) and (rhs_ty_tag == .int or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {
  32204             if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| {
  32205                 return if (res) .bool_true else .bool_false;
  32206             }
  32207         }
  32208         // Result based on NaN comparison
  32209         if (rhs_val.isNan(zcu)) {
  32210             return if (op == .neq) .bool_true else .bool_false;
  32211         }
  32212         // Result based on inf comparison to int
  32213         if (rhs_val.isInf(zcu) and lhs_ty_tag == .int) return switch (op) {
  32214             .neq => .bool_true,
  32215             .eq => .bool_false,
  32216             .gt, .gte => if (rhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
  32217             .lt, .lte => if (rhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
  32218         };
  32219     }
  32220 
  32221     // Any other comparison depends on both values, so the result is undef if either is undef.
  32222     if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
  32223     if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
  32224 
  32225     const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: {
  32226         if (maybe_rhs_val) |rhs_val| {
  32227             const res = try Value.compareHeteroSema(lhs_val, op, rhs_val, pt);
  32228             return if (res) .bool_true else .bool_false;
  32229         } else break :rs rhs_src;
  32230     } else lhs_src;
  32231 
  32232     // TODO handle comparisons against lazy zero values
  32233     // Some values can be compared against zero without being runtime-known or without forcing
  32234     // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to
  32235     // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout
  32236     // of this function if we don't need to.
  32237     try sema.requireRuntimeBlock(block, src, runtime_src);
  32238 
  32239     // For floats, emit a float comparison instruction.
  32240     const lhs_is_float = switch (lhs_ty_tag) {
  32241         .float, .comptime_float => true,
  32242         else => false,
  32243     };
  32244     const rhs_is_float = switch (rhs_ty_tag) {
  32245         .float, .comptime_float => true,
  32246         else => false,
  32247     };
  32248 
  32249     if (lhs_is_float and rhs_is_float) {
  32250         // Smaller fixed-width floats coerce to larger fixed-width floats.
  32251         // comptime_float coerces to fixed-width float.
  32252         const dest_ty = x: {
  32253             if (lhs_ty_tag == .comptime_float) {
  32254                 break :x rhs_ty;
  32255             } else if (rhs_ty_tag == .comptime_float) {
  32256                 break :x lhs_ty;
  32257             }
  32258             if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) {
  32259                 break :x lhs_ty;
  32260             } else {
  32261                 break :x rhs_ty;
  32262             }
  32263         };
  32264         const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  32265         const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  32266         return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs);
  32267     }
  32268 
  32269     // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
  32270     // For mixed signed and unsigned integers, implicit cast both operands to a signed
  32271     // integer with + 1 bit.
  32272     // For mixed floats and integers, extract the integer part from the float, cast that to
  32273     // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
  32274     // add/subtract 1.
  32275     const lhs_is_signed = if (maybe_lhs_val) |lhs_val|
  32276         !(try lhs_val.compareAllWithZeroSema(.gte, pt))
  32277     else
  32278         (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu));
  32279     const rhs_is_signed = if (maybe_rhs_val) |rhs_val|
  32280         !(try rhs_val.compareAllWithZeroSema(.gte, pt))
  32281     else
  32282         (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu));
  32283     const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
  32284 
  32285     var dest_float_type: ?Type = null;
  32286 
  32287     var lhs_bits: usize = undefined;
  32288     if (maybe_lhs_val) |unresolved_lhs_val| {
  32289         const lhs_val = try sema.resolveLazyValue(unresolved_lhs_val);
  32290         if (!rhs_is_signed) {
  32291             switch (lhs_val.orderAgainstZero(zcu)) {
  32292                 .gt => {},
  32293                 .eq => switch (op) { // LHS = 0, RHS is unsigned
  32294                     .lte => return .bool_true,
  32295                     .gt => return .bool_false,
  32296                     else => {},
  32297                 },
  32298                 .lt => switch (op) { // LHS < 0, RHS is unsigned
  32299                     .neq, .lt, .lte => return .bool_true,
  32300                     .eq, .gt, .gte => return .bool_false,
  32301                 },
  32302             }
  32303         }
  32304         if (lhs_is_float) {
  32305             const float = lhs_val.toFloat(f128, zcu);
  32306             var big_int: std.math.big.int.Mutable = .{
  32307                 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)),
  32308                 .len = undefined,
  32309                 .positive = undefined,
  32310             };
  32311             switch (big_int.setFloat(float, .away)) {
  32312                 .inexact => switch (op) {
  32313                     .eq => return .bool_false,
  32314                     .neq => return .bool_true,
  32315                     else => {},
  32316                 },
  32317                 .exact => {},
  32318             }
  32319             lhs_bits = big_int.toConst().bitCountTwosComp();
  32320         } else {
  32321             lhs_bits = lhs_val.intBitCountTwosComp(zcu);
  32322         }
  32323         lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed);
  32324     } else if (lhs_is_float) {
  32325         dest_float_type = lhs_ty;
  32326     } else {
  32327         const int_info = lhs_ty.intInfo(zcu);
  32328         lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  32329     }
  32330 
  32331     var rhs_bits: usize = undefined;
  32332     if (maybe_rhs_val) |unresolved_rhs_val| {
  32333         const rhs_val = try sema.resolveLazyValue(unresolved_rhs_val);
  32334         if (!lhs_is_signed) {
  32335             switch (rhs_val.orderAgainstZero(zcu)) {
  32336                 .gt => {},
  32337                 .eq => switch (op) { // RHS = 0, LHS is unsigned
  32338                     .gte => return .bool_true,
  32339                     .lt => return .bool_false,
  32340                     else => {},
  32341                 },
  32342                 .lt => switch (op) { // RHS < 0, LHS is unsigned
  32343                     .neq, .gt, .gte => return .bool_true,
  32344                     .eq, .lt, .lte => return .bool_false,
  32345                 },
  32346             }
  32347         }
  32348         if (rhs_is_float) {
  32349             const float = rhs_val.toFloat(f128, zcu);
  32350             var big_int: std.math.big.int.Mutable = .{
  32351                 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)),
  32352                 .len = undefined,
  32353                 .positive = undefined,
  32354             };
  32355             switch (big_int.setFloat(float, .away)) {
  32356                 .inexact => switch (op) {
  32357                     .eq => return .bool_false,
  32358                     .neq => return .bool_true,
  32359                     else => {},
  32360                 },
  32361                 .exact => {},
  32362             }
  32363             rhs_bits = big_int.toConst().bitCountTwosComp();
  32364         } else {
  32365             rhs_bits = rhs_val.intBitCountTwosComp(zcu);
  32366         }
  32367         rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed);
  32368     } else if (rhs_is_float) {
  32369         dest_float_type = rhs_ty;
  32370     } else {
  32371         const int_info = rhs_ty.intInfo(zcu);
  32372         rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
  32373     }
  32374 
  32375     const dest_ty = if (dest_float_type) |ft| ft else blk: {
  32376         const max_bits = @max(lhs_bits, rhs_bits);
  32377         const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits});
  32378         const signedness: std.builtin.Signedness = if (dest_int_is_signed) .signed else .unsigned;
  32379         break :blk try pt.intType(signedness, casted_bits);
  32380     };
  32381     const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
  32382     const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
  32383 
  32384     return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs);
  32385 }
  32386 
  32387 /// Asserts that LHS value is an int or comptime int and not undefined, and
  32388 /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to
  32389 /// determine whether `op` has a guaranteed result.
  32390 /// If it cannot be determined, returns null.
  32391 /// Otherwise returns a bool for the guaranteed comparison operation.
  32392 fn compareIntsOnlyPossibleResult(
  32393     sema: *Sema,
  32394     lhs_val: Value,
  32395     op: std.math.CompareOperator,
  32396     rhs_ty: Type,
  32397 ) SemaError!?bool {
  32398     const pt = sema.pt;
  32399     const zcu = pt.zcu;
  32400 
  32401     const min_rhs = try rhs_ty.minInt(pt, rhs_ty);
  32402     const max_rhs = try rhs_ty.maxInt(pt, rhs_ty);
  32403 
  32404     if (min_rhs.toIntern() == max_rhs.toIntern()) {
  32405         // RHS is effectively comptime-known.
  32406         return try Value.compareHeteroSema(lhs_val, op, min_rhs, pt);
  32407     }
  32408 
  32409     const against_min = try lhs_val.orderAdvanced(min_rhs, .sema, zcu, pt.tid);
  32410     const against_max = try lhs_val.orderAdvanced(max_rhs, .sema, zcu, pt.tid);
  32411 
  32412     switch (op) {
  32413         .eq => {
  32414             if (against_min.compare(.lt)) return false;
  32415             if (against_max.compare(.gt)) return false;
  32416         },
  32417         .neq => {
  32418             if (against_min.compare(.lt)) return true;
  32419             if (against_max.compare(.gt)) return true;
  32420         },
  32421         .lt => {
  32422             if (against_min.compare(.lt)) return true;
  32423             if (against_max.compare(.gte)) return false;
  32424         },
  32425         .gt => {
  32426             if (against_max.compare(.gt)) return true;
  32427             if (against_min.compare(.lte)) return false;
  32428         },
  32429         .lte => {
  32430             if (against_min.compare(.lte)) return true;
  32431             if (against_max.compare(.gt)) return false;
  32432         },
  32433         .gte => {
  32434             if (against_max.compare(.gte)) return true;
  32435             if (against_min.compare(.lt)) return false;
  32436         },
  32437     }
  32438 
  32439     return null;
  32440 }
  32441 
  32442 /// Asserts that lhs and rhs types are both vectors.
  32443 fn cmpVector(
  32444     sema: *Sema,
  32445     block: *Block,
  32446     src: LazySrcLoc,
  32447     lhs: Air.Inst.Ref,
  32448     rhs: Air.Inst.Ref,
  32449     op: std.math.CompareOperator,
  32450     lhs_src: LazySrcLoc,
  32451     rhs_src: LazySrcLoc,
  32452 ) CompileError!Air.Inst.Ref {
  32453     const pt = sema.pt;
  32454     const zcu = pt.zcu;
  32455     const lhs_ty = sema.typeOf(lhs);
  32456     const rhs_ty = sema.typeOf(rhs);
  32457     assert(lhs_ty.zigTypeTag(zcu) == .vector);
  32458     assert(rhs_ty.zigTypeTag(zcu) == .vector);
  32459     try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
  32460 
  32461     const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } });
  32462     const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src);
  32463     const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src);
  32464 
  32465     const result_ty = try pt.vectorType(.{
  32466         .len = lhs_ty.vectorLen(zcu),
  32467         .child = .bool_type,
  32468     });
  32469 
  32470     const maybe_lhs_val = try sema.resolveValue(casted_lhs);
  32471     const maybe_rhs_val = try sema.resolveValue(casted_rhs);
  32472     if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
  32473     if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
  32474 
  32475     const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: {
  32476         if (maybe_rhs_val) |rhs_val| {
  32477             const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
  32478             return Air.internedToRef(cmp_val.toIntern());
  32479         } else break :src rhs_src;
  32480     } else lhs_src;
  32481 
  32482     try sema.requireRuntimeBlock(block, src, runtime_src);
  32483     return block.addCmpVector(casted_lhs, casted_rhs, op);
  32484 }
  32485 
  32486 fn wrapOptional(
  32487     sema: *Sema,
  32488     block: *Block,
  32489     dest_ty: Type,
  32490     inst: Air.Inst.Ref,
  32491     inst_src: LazySrcLoc,
  32492 ) !Air.Inst.Ref {
  32493     if (try sema.resolveValue(inst)) |val| {
  32494         return Air.internedToRef((try sema.pt.intern(.{ .opt = .{
  32495             .ty = dest_ty.toIntern(),
  32496             .val = val.toIntern(),
  32497         } })));
  32498     }
  32499 
  32500     try sema.requireRuntimeBlock(block, inst_src, null);
  32501     return block.addTyOp(.wrap_optional, dest_ty, inst);
  32502 }
  32503 
  32504 fn wrapErrorUnionPayload(
  32505     sema: *Sema,
  32506     block: *Block,
  32507     dest_ty: Type,
  32508     inst: Air.Inst.Ref,
  32509     inst_src: LazySrcLoc,
  32510 ) !Air.Inst.Ref {
  32511     const pt = sema.pt;
  32512     const zcu = pt.zcu;
  32513     const dest_payload_ty = dest_ty.errorUnionPayload(zcu);
  32514     const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false });
  32515     if (try sema.resolveValue(coerced)) |val| {
  32516         return Air.internedToRef((try pt.intern(.{ .error_union = .{
  32517             .ty = dest_ty.toIntern(),
  32518             .val = .{ .payload = val.toIntern() },
  32519         } })));
  32520     }
  32521     try sema.requireRuntimeBlock(block, inst_src, null);
  32522     return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
  32523 }
  32524 
  32525 fn wrapErrorUnionSet(
  32526     sema: *Sema,
  32527     block: *Block,
  32528     dest_ty: Type,
  32529     inst: Air.Inst.Ref,
  32530     inst_src: LazySrcLoc,
  32531 ) !Air.Inst.Ref {
  32532     const pt = sema.pt;
  32533     const zcu = pt.zcu;
  32534     const ip = &zcu.intern_pool;
  32535     const inst_ty = sema.typeOf(inst);
  32536     const dest_err_set_ty = dest_ty.errorUnionSet(zcu);
  32537     if (try sema.resolveValue(inst)) |val| {
  32538         const expected_name = zcu.intern_pool.indexToKey(val.toIntern()).err.name;
  32539         switch (dest_err_set_ty.toIntern()) {
  32540             .anyerror_type => {},
  32541             .adhoc_inferred_error_set_type => ok: {
  32542                 const ies = sema.fn_ret_ty_ies.?;
  32543                 switch (ies.resolved) {
  32544                     .anyerror_type => break :ok,
  32545                     .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) {
  32546                         break :ok;
  32547                     },
  32548                     else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) {
  32549                         break :ok;
  32550                     },
  32551                 }
  32552                 return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  32553             },
  32554             else => switch (ip.indexToKey(dest_err_set_ty.toIntern())) {
  32555                 .error_set_type => |error_set_type| ok: {
  32556                     if (error_set_type.nameIndex(ip, expected_name) != null) break :ok;
  32557                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  32558                 },
  32559                 .inferred_error_set_type => |func_index| ok: {
  32560                     // We carefully do this in an order that avoids unnecessarily
  32561                     // resolving the destination error set type.
  32562                     try zcu.maybeUnresolveIes(func_index);
  32563                     switch (ip.funcIesResolvedUnordered(func_index)) {
  32564                         .anyerror_type => break :ok,
  32565                         .none => if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, dest_err_set_ty, inst_ty, inst_src, inst_src)) {
  32566                             break :ok;
  32567                         },
  32568                         else => |i| if (ip.indexToKey(i).error_set_type.nameIndex(ip, expected_name) != null) {
  32569                             break :ok;
  32570                         },
  32571                     }
  32572 
  32573                     return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
  32574                 },
  32575                 else => unreachable,
  32576             },
  32577         }
  32578         return Air.internedToRef((try pt.intern(.{ .error_union = .{
  32579             .ty = dest_ty.toIntern(),
  32580             .val = .{ .err_name = expected_name },
  32581         } })));
  32582     }
  32583 
  32584     try sema.requireRuntimeBlock(block, inst_src, null);
  32585     const coerced = try sema.coerce(block, dest_err_set_ty, inst, inst_src);
  32586     return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
  32587 }
  32588 
  32589 fn unionToTag(
  32590     sema: *Sema,
  32591     block: *Block,
  32592     enum_ty: Type,
  32593     un: Air.Inst.Ref,
  32594     un_src: LazySrcLoc,
  32595 ) !Air.Inst.Ref {
  32596     const pt = sema.pt;
  32597     const zcu = pt.zcu;
  32598     if ((try sema.typeHasOnePossibleValue(enum_ty))) |opv| {
  32599         return Air.internedToRef(opv.toIntern());
  32600     }
  32601     if (try sema.resolveValue(un)) |un_val| {
  32602         const tag_val = un_val.unionTag(zcu).?;
  32603         if (tag_val.isUndef(zcu))
  32604             return try pt.undefRef(enum_ty);
  32605         return Air.internedToRef(tag_val.toIntern());
  32606     }
  32607     try sema.requireRuntimeBlock(block, un_src, null);
  32608     return block.addTyOp(.get_union_tag, enum_ty, un);
  32609 }
  32610 
  32611 const PeerResolveStrategy = enum {
  32612     /// The type is not known.
  32613     /// If refined no further, this is equivalent to `exact`.
  32614     unknown,
  32615     /// The type may be an error set or error union.
  32616     /// If refined no further, it is an error set.
  32617     error_set,
  32618     /// The type must be some error union.
  32619     error_union,
  32620     /// The type may be @TypeOf(null), an optional or a C pointer.
  32621     /// If refined no further, it is @TypeOf(null).
  32622     nullable,
  32623     /// The type must be some optional or a C pointer.
  32624     /// If refined no further, it is an optional.
  32625     optional,
  32626     /// The type must be either an array or a vector.
  32627     /// If refined no further, it is an array.
  32628     array,
  32629     /// The type must be a vector.
  32630     vector,
  32631     /// The type must be a C pointer.
  32632     c_ptr,
  32633     /// The type must be a pointer (C or not).
  32634     /// If refined no further, it is a non-C pointer.
  32635     ptr,
  32636     /// The type must be a function or a pointer to a function.
  32637     /// If refined no further, it is a function.
  32638     func,
  32639     /// The type must be an enum literal, or some specific enum or union. Which one is decided
  32640     /// afterwards based on the types in question.
  32641     enum_or_union,
  32642     /// The type must be some integer or float type.
  32643     /// If refined no further, it is `comptime_int`.
  32644     comptime_int,
  32645     /// The type must be some float type.
  32646     /// If refined no further, it is `comptime_float`.
  32647     comptime_float,
  32648     /// The type must be some float or fixed-width integer type.
  32649     /// If refined no further, it is some fixed-width integer type.
  32650     fixed_int,
  32651     /// The type must be some fixed-width float type.
  32652     fixed_float,
  32653     /// The type must be a tuple.
  32654     tuple,
  32655     /// The peers must all be of the same type.
  32656     exact,
  32657 
  32658     /// Given two strategies, find a strategy that satisfies both, if one exists. If no such
  32659     /// strategy exists, any strategy may be returned; an error will be emitted when the caller
  32660     /// attempts to use the strategy to resolve the type.
  32661     /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at
  32662     /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy.
  32663     fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy {
  32664         // Our merging should be order-independent. Thus, even though the union order is arbitrary,
  32665         // by sorting the tags and switching first on the smaller, we have half as many cases to
  32666         // worry about (since we avoid the duplicates).
  32667         const s0_is_a = @intFromEnum(a) <= @intFromEnum(b);
  32668         const s0 = if (s0_is_a) a else b;
  32669         const s1 = if (s0_is_a) b else a;
  32670 
  32671         const ReasonMethod = enum {
  32672             all_s0,
  32673             all_s1,
  32674             either,
  32675         };
  32676 
  32677         const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) {
  32678             .unknown => .{ .all_s1, s1 },
  32679             .error_set => switch (s1) {
  32680                 .error_set => .{ .either, .error_set },
  32681                 else => .{ .all_s0, .error_union },
  32682             },
  32683             .error_union => switch (s1) {
  32684                 .error_union => .{ .either, .error_union },
  32685                 else => .{ .all_s0, .error_union },
  32686             },
  32687             .nullable => switch (s1) {
  32688                 .nullable => .{ .either, .nullable },
  32689                 .c_ptr => .{ .all_s1, .c_ptr },
  32690                 else => .{ .all_s0, .optional },
  32691             },
  32692             .optional => switch (s1) {
  32693                 .optional => .{ .either, .optional },
  32694                 .c_ptr => .{ .all_s1, .c_ptr },
  32695                 else => .{ .all_s0, .optional },
  32696             },
  32697             .array => switch (s1) {
  32698                 .array => .{ .either, .array },
  32699                 .vector => .{ .all_s1, .vector },
  32700                 else => .{ .all_s0, .array },
  32701             },
  32702             .vector => switch (s1) {
  32703                 .vector => .{ .either, .vector },
  32704                 else => .{ .all_s0, .vector },
  32705             },
  32706             .c_ptr => switch (s1) {
  32707                 .c_ptr => .{ .either, .c_ptr },
  32708                 else => .{ .all_s0, .c_ptr },
  32709             },
  32710             .ptr => switch (s1) {
  32711                 .ptr => .{ .either, .ptr },
  32712                 else => .{ .all_s0, .ptr },
  32713             },
  32714             .func => switch (s1) {
  32715                 .func => .{ .either, .func },
  32716                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32717             },
  32718             .enum_or_union => switch (s1) {
  32719                 .enum_or_union => .{ .either, .enum_or_union },
  32720                 else => .{ .all_s0, .enum_or_union },
  32721             },
  32722             .comptime_int => switch (s1) {
  32723                 .comptime_int => .{ .either, .comptime_int },
  32724                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32725             },
  32726             .comptime_float => switch (s1) {
  32727                 .comptime_float => .{ .either, .comptime_float },
  32728                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32729             },
  32730             .fixed_int => switch (s1) {
  32731                 .fixed_int => .{ .either, .fixed_int },
  32732                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32733             },
  32734             .fixed_float => switch (s1) {
  32735                 .fixed_float => .{ .either, .fixed_float },
  32736                 else => .{ .all_s1, s1 }, // doesn't override anything later
  32737             },
  32738             .tuple => switch (s1) {
  32739                 .exact => .{ .all_s1, .exact },
  32740                 else => .{ .all_s0, .tuple },
  32741             },
  32742             .exact => .{ .all_s0, .exact },
  32743         };
  32744 
  32745         switch (reason_method) {
  32746             .all_s0 => {
  32747                 if (!s0_is_a) {
  32748                     reason_peer.* = b_peer_idx;
  32749                 }
  32750             },
  32751             .all_s1 => {
  32752                 if (s0_is_a) {
  32753                     reason_peer.* = b_peer_idx;
  32754                 }
  32755             },
  32756             .either => {
  32757                 // Prefer the earliest peer
  32758                 reason_peer.* = @min(reason_peer.*, b_peer_idx);
  32759             },
  32760         }
  32761 
  32762         return strat;
  32763     }
  32764 
  32765     fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy {
  32766         return switch (ty.zigTypeTag(zcu)) {
  32767             .type, .void, .bool, .@"opaque", .frame, .@"anyframe" => .exact,
  32768             .noreturn, .undefined => .unknown,
  32769             .null => .nullable,
  32770             .comptime_int => .comptime_int,
  32771             .int => .fixed_int,
  32772             .comptime_float => .comptime_float,
  32773             .float => .fixed_float,
  32774             .pointer => if (ty.ptrInfo(zcu).flags.size == .c) .c_ptr else .ptr,
  32775             .array => .array,
  32776             .vector => .vector,
  32777             .optional => .optional,
  32778             .error_set => .error_set,
  32779             .error_union => .error_union,
  32780             .enum_literal, .@"enum", .@"union" => .enum_or_union,
  32781             .@"struct" => if (ty.isTuple(zcu)) .tuple else .exact,
  32782             .@"fn" => .func,
  32783         };
  32784     }
  32785 };
  32786 
  32787 const PeerTypeCandidateSrc = union(enum) {
  32788     /// Do not print out error notes for candidate sources
  32789     none: void,
  32790     /// When we want to know the the src of candidate i, look up at
  32791     /// index i in this slice
  32792     override: []const ?LazySrcLoc,
  32793     /// resolvePeerTypes originates from a @TypeOf(...) call
  32794     typeof_builtin_call_node_offset: std.zig.Ast.Node.Offset,
  32795 
  32796     pub fn resolve(
  32797         self: PeerTypeCandidateSrc,
  32798         block: *Block,
  32799         candidate_i: usize,
  32800     ) ?LazySrcLoc {
  32801         return switch (self) {
  32802             .none => null,
  32803             .override => |candidate_srcs| if (candidate_i >= candidate_srcs.len)
  32804                 null
  32805             else
  32806                 candidate_srcs[candidate_i],
  32807             .typeof_builtin_call_node_offset => |node_offset| block.builtinCallArgSrc(node_offset, @intCast(candidate_i)),
  32808         };
  32809     }
  32810 };
  32811 
  32812 const PeerResolveResult = union(enum) {
  32813     /// The peer type resolution was successful, and resulted in the given type.
  32814     success: Type,
  32815     /// There was some generic conflict between two peers.
  32816     conflict: struct {
  32817         peer_idx_a: usize,
  32818         peer_idx_b: usize,
  32819     },
  32820     /// There was an error when resolving the type of a struct or tuple field.
  32821     field_error: struct {
  32822         /// The name of the field which caused the failure.
  32823         field_name: InternPool.NullTerminatedString,
  32824         /// The type of this field in each peer.
  32825         field_types: []Type,
  32826         /// The error from resolving the field type. Guaranteed not to be `success`.
  32827         sub_result: *PeerResolveResult,
  32828     },
  32829 
  32830     fn report(
  32831         result: PeerResolveResult,
  32832         sema: *Sema,
  32833         block: *Block,
  32834         src: LazySrcLoc,
  32835         instructions: []const Air.Inst.Ref,
  32836         candidate_srcs: PeerTypeCandidateSrc,
  32837     ) !*Zcu.ErrorMsg {
  32838         const pt = sema.pt;
  32839 
  32840         var opt_msg: ?*Zcu.ErrorMsg = null;
  32841         errdefer if (opt_msg) |msg| msg.destroy(sema.gpa);
  32842 
  32843         // If we mention fields we'll want to include field types, so put peer types in a buffer
  32844         var peer_tys = try sema.arena.alloc(Type, instructions.len);
  32845         for (peer_tys, instructions) |*ty, inst| {
  32846             ty.* = sema.typeOf(inst);
  32847         }
  32848 
  32849         var cur = result;
  32850         while (true) {
  32851             var conflict_idx: [2]usize = undefined;
  32852 
  32853             switch (cur) {
  32854                 .success => unreachable,
  32855                 .conflict => |conflict| {
  32856                     // Fall through to two-peer conflict handling below
  32857                     conflict_idx = .{
  32858                         conflict.peer_idx_a,
  32859                         conflict.peer_idx_b,
  32860                     };
  32861                 },
  32862                 .field_error => |field_error| {
  32863                     const fmt = "struct field '{f}' has conflicting types";
  32864                     const args = .{field_error.field_name.fmt(&pt.zcu.intern_pool)};
  32865                     if (opt_msg) |msg| {
  32866                         try sema.errNote(src, msg, fmt, args);
  32867                     } else {
  32868                         opt_msg = try sema.errMsg(src, fmt, args);
  32869                     }
  32870 
  32871                     // Continue on to child error
  32872                     cur = field_error.sub_result.*;
  32873                     peer_tys = field_error.field_types;
  32874                     continue;
  32875                 },
  32876             }
  32877 
  32878             // This is the path for reporting a generic conflict between two peers.
  32879 
  32880             if (conflict_idx[1] < conflict_idx[0]) {
  32881                 // b comes first in source, so it's better if it comes first in the error
  32882                 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]);
  32883             }
  32884 
  32885             const conflict_tys: [2]Type = .{
  32886                 peer_tys[conflict_idx[0]],
  32887                 peer_tys[conflict_idx[1]],
  32888             };
  32889             const conflict_srcs: [2]?LazySrcLoc = .{
  32890                 candidate_srcs.resolve(block, conflict_idx[0]),
  32891                 candidate_srcs.resolve(block, conflict_idx[1]),
  32892             };
  32893 
  32894             const fmt = "incompatible types: '{f}' and '{f}'";
  32895             const args = .{
  32896                 conflict_tys[0].fmt(pt),
  32897                 conflict_tys[1].fmt(pt),
  32898             };
  32899             const msg = if (opt_msg) |msg| msg: {
  32900                 try sema.errNote(src, msg, fmt, args);
  32901                 break :msg msg;
  32902             } else msg: {
  32903                 const msg = try sema.errMsg(src, fmt, args);
  32904                 opt_msg = msg;
  32905                 break :msg msg;
  32906             };
  32907 
  32908             if (conflict_srcs[0]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[0].fmt(pt)});
  32909             if (conflict_srcs[1]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[1].fmt(pt)});
  32910 
  32911             // No child error
  32912             break;
  32913         }
  32914 
  32915         return opt_msg.?;
  32916     }
  32917 };
  32918 
  32919 fn resolvePeerTypes(
  32920     sema: *Sema,
  32921     block: *Block,
  32922     src: LazySrcLoc,
  32923     instructions: []const Air.Inst.Ref,
  32924     candidate_srcs: PeerTypeCandidateSrc,
  32925 ) !Type {
  32926     switch (instructions.len) {
  32927         0 => return .noreturn,
  32928         1 => return sema.typeOf(instructions[0]),
  32929         else => {},
  32930     }
  32931 
  32932     // Fast path: check if everything has the same type to bypass the main PTR logic.
  32933     same_type: {
  32934         const ty = sema.typeOf(instructions[0]);
  32935         for (instructions[1..]) |inst| {
  32936             if (sema.typeOf(inst).toIntern() != ty.toIntern()) {
  32937                 break :same_type;
  32938             }
  32939         }
  32940         return ty;
  32941     }
  32942 
  32943     const peer_tys = try sema.arena.alloc(?Type, instructions.len);
  32944     const peer_vals = try sema.arena.alloc(?Value, instructions.len);
  32945 
  32946     for (instructions, peer_tys, peer_vals) |inst, *ty, *val| {
  32947         ty.* = sema.typeOf(inst);
  32948         val.* = try sema.resolveValue(inst);
  32949     }
  32950 
  32951     switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) {
  32952         .success => |ty| return ty,
  32953         else => |result| {
  32954             const msg = try result.report(sema, block, src, instructions, candidate_srcs);
  32955             return sema.failWithOwnedErrorMsg(block, msg);
  32956         },
  32957     }
  32958 }
  32959 
  32960 fn resolvePeerTypesInner(
  32961     sema: *Sema,
  32962     block: *Block,
  32963     src: LazySrcLoc,
  32964     peer_tys: []?Type,
  32965     peer_vals: []?Value,
  32966 ) !PeerResolveResult {
  32967     const pt = sema.pt;
  32968     const zcu = pt.zcu;
  32969     const ip = &zcu.intern_pool;
  32970 
  32971     var strat_reason: usize = 0;
  32972     var s: PeerResolveStrategy = .unknown;
  32973     for (peer_tys, 0..) |opt_ty, i| {
  32974         const ty = opt_ty orelse continue;
  32975         s = s.merge(PeerResolveStrategy.select(ty, zcu), &strat_reason, i);
  32976     }
  32977 
  32978     if (s == .unknown) {
  32979         // The whole thing was noreturn or undefined - try to do an exact match
  32980         s = .exact;
  32981     } else {
  32982         // There was something other than noreturn and undefined, so we can ignore those peers
  32983         for (peer_tys) |*ty_ptr| {
  32984             const ty = ty_ptr.* orelse continue;
  32985             switch (ty.zigTypeTag(zcu)) {
  32986                 .noreturn, .undefined => ty_ptr.* = null,
  32987                 else => {},
  32988             }
  32989         }
  32990     }
  32991 
  32992     const target = zcu.getTarget();
  32993 
  32994     switch (s) {
  32995         .unknown => unreachable,
  32996 
  32997         .error_set => {
  32998             var final_set: ?Type = null;
  32999             for (peer_tys, 0..) |opt_ty, i| {
  33000                 const ty = opt_ty orelse continue;
  33001                 if (ty.zigTypeTag(zcu) != .error_set) return .{ .conflict = .{
  33002                     .peer_idx_a = strat_reason,
  33003                     .peer_idx_b = i,
  33004                 } };
  33005                 if (final_set) |cur_set| {
  33006                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty);
  33007                 } else {
  33008                     final_set = ty;
  33009                 }
  33010             }
  33011             return .{ .success = final_set.? };
  33012         },
  33013 
  33014         .error_union => {
  33015             var final_set: ?Type = null;
  33016             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  33017                 const ty = ty_ptr.* orelse continue;
  33018                 const set_ty = switch (ty.zigTypeTag(zcu)) {
  33019                     .error_set => blk: {
  33020                         ty_ptr.* = null; // no payload to decide on
  33021                         val_ptr.* = null;
  33022                         break :blk ty;
  33023                     },
  33024                     .error_union => blk: {
  33025                         const set_ty = ty.errorUnionSet(zcu);
  33026                         ty_ptr.* = ty.errorUnionPayload(zcu);
  33027                         if (val_ptr.*) |eu_val| switch (ip.indexToKey(eu_val.toIntern())) {
  33028                             .error_union => |eu| switch (eu.val) {
  33029                                 .payload => |payload_ip| val_ptr.* = Value.fromInterned(payload_ip),
  33030                                 .err_name => val_ptr.* = null,
  33031                             },
  33032                             .undef => val_ptr.* = Value.fromInterned(try pt.intern(.{ .undef = ty_ptr.*.?.toIntern() })),
  33033                             else => unreachable,
  33034                         };
  33035                         break :blk set_ty;
  33036                     },
  33037                     else => continue, // whole type is the payload
  33038                 };
  33039                 if (final_set) |cur_set| {
  33040                     final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty);
  33041                 } else {
  33042                     final_set = set_ty;
  33043                 }
  33044             }
  33045             assert(final_set != null);
  33046             const final_payload = switch (try sema.resolvePeerTypesInner(
  33047                 block,
  33048                 src,
  33049                 peer_tys,
  33050                 peer_vals,
  33051             )) {
  33052                 .success => |ty| ty,
  33053                 else => |result| return result,
  33054             };
  33055             return .{ .success = try pt.errorUnionType(final_set.?, final_payload) };
  33056         },
  33057 
  33058         .nullable => {
  33059             for (peer_tys, 0..) |opt_ty, i| {
  33060                 const ty = opt_ty orelse continue;
  33061                 if (!ty.eql(.null, zcu)) return .{ .conflict = .{
  33062                     .peer_idx_a = strat_reason,
  33063                     .peer_idx_b = i,
  33064                 } };
  33065             }
  33066             return .{ .success = .null };
  33067         },
  33068 
  33069         .optional => {
  33070             for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
  33071                 const ty = ty_ptr.* orelse continue;
  33072                 switch (ty.zigTypeTag(zcu)) {
  33073                     .null => {
  33074                         ty_ptr.* = null;
  33075                         val_ptr.* = null;
  33076                     },
  33077                     .optional => {
  33078                         ty_ptr.* = ty.optionalChild(zcu);
  33079                         if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(zcu)) opt_val.optionalValue(zcu) else null;
  33080                     },
  33081                     else => {},
  33082                 }
  33083             }
  33084             const child_ty = switch (try sema.resolvePeerTypesInner(
  33085                 block,
  33086                 src,
  33087                 peer_tys,
  33088                 peer_vals,
  33089             )) {
  33090                 .success => |ty| ty,
  33091                 else => |result| return result,
  33092             };
  33093             return .{ .success = try pt.optionalType(child_ty.toIntern()) };
  33094         },
  33095 
  33096         .array => {
  33097             // Index of the first non-null peer
  33098             var opt_first_idx: ?usize = null;
  33099             // Index of the first array or vector peer (i.e. not a tuple)
  33100             var opt_first_arr_idx: ?usize = null;
  33101             // Set to non-null once we see any peer, even a tuple
  33102             var len: u64 = undefined;
  33103             var sentinel: ?Value = undefined;
  33104             // Only set once we see a non-tuple peer
  33105             var elem_ty: Type = undefined;
  33106 
  33107             for (peer_tys, 0..) |*ty_ptr, i| {
  33108                 const ty = ty_ptr.* orelse continue;
  33109 
  33110                 if (!ty.isArrayOrVector(zcu)) {
  33111                     // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced.
  33112                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  33113                         .peer_idx_a = strat_reason,
  33114                         .peer_idx_b = i,
  33115                     } };
  33116 
  33117                     if (opt_first_idx) |first_idx| {
  33118                         if (arr_like.len != len) return .{ .conflict = .{
  33119                             .peer_idx_a = first_idx,
  33120                             .peer_idx_b = i,
  33121                         } };
  33122                     } else {
  33123                         opt_first_idx = i;
  33124                         len = arr_like.len;
  33125                     }
  33126 
  33127                     sentinel = null;
  33128 
  33129                     continue;
  33130                 }
  33131 
  33132                 const first_arr_idx = opt_first_arr_idx orelse {
  33133                     if (opt_first_idx == null) {
  33134                         opt_first_idx = i;
  33135                         len = ty.arrayLen(zcu);
  33136                         sentinel = ty.sentinel(zcu);
  33137                     }
  33138                     opt_first_arr_idx = i;
  33139                     elem_ty = ty.childType(zcu);
  33140                     continue;
  33141                 };
  33142 
  33143                 if (ty.arrayLen(zcu) != len) return .{ .conflict = .{
  33144                     .peer_idx_a = first_arr_idx,
  33145                     .peer_idx_b = i,
  33146                 } };
  33147 
  33148                 const peer_elem_ty = ty.childType(zcu);
  33149                 if (!peer_elem_ty.eql(elem_ty, zcu)) coerce: {
  33150                     const peer_elem_coerces_to_elem =
  33151                         try sema.coerceInMemoryAllowed(block, elem_ty, peer_elem_ty, false, zcu.getTarget(), src, src, null);
  33152                     if (peer_elem_coerces_to_elem == .ok) {
  33153                         break :coerce;
  33154                     }
  33155 
  33156                     const elem_coerces_to_peer_elem =
  33157                         try sema.coerceInMemoryAllowed(block, peer_elem_ty, elem_ty, false, zcu.getTarget(), src, src, null);
  33158                     if (elem_coerces_to_peer_elem == .ok) {
  33159                         elem_ty = peer_elem_ty;
  33160                         break :coerce;
  33161                     }
  33162 
  33163                     return .{ .conflict = .{
  33164                         .peer_idx_a = first_arr_idx,
  33165                         .peer_idx_b = i,
  33166                     } };
  33167                 }
  33168 
  33169                 if (sentinel) |cur_sent| {
  33170                     if (ty.sentinel(zcu)) |peer_sent| {
  33171                         if (!peer_sent.eql(cur_sent, elem_ty, zcu)) sentinel = null;
  33172                     } else {
  33173                         sentinel = null;
  33174                     }
  33175                 }
  33176             }
  33177 
  33178             // There should always be at least one array or vector peer
  33179             assert(opt_first_arr_idx != null);
  33180 
  33181             return .{ .success = try pt.arrayType(.{
  33182                 .len = len,
  33183                 .child = elem_ty.toIntern(),
  33184                 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none,
  33185             }) };
  33186         },
  33187 
  33188         .vector => {
  33189             var len: ?u64 = null;
  33190             var first_idx: usize = undefined;
  33191             for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| {
  33192                 const ty = ty_ptr.* orelse continue;
  33193 
  33194                 if (!ty.isArrayOrVector(zcu)) {
  33195                     // Allow tuples of the correct length
  33196                     const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
  33197                         .peer_idx_a = strat_reason,
  33198                         .peer_idx_b = i,
  33199                     } };
  33200 
  33201                     if (len) |expect_len| {
  33202                         if (arr_like.len != expect_len) return .{ .conflict = .{
  33203                             .peer_idx_a = first_idx,
  33204                             .peer_idx_b = i,
  33205                         } };
  33206                     } else {
  33207                         len = arr_like.len;
  33208                         first_idx = i;
  33209                     }
  33210 
  33211                     // Tuples won't participate in the child type resolution. We'll resolve without
  33212                     // them, and if the tuples have a bad type, we'll get a coercion error later.
  33213                     ty_ptr.* = null;
  33214                     val_ptr.* = null;
  33215 
  33216                     continue;
  33217                 }
  33218 
  33219                 if (len) |expect_len| {
  33220                     if (ty.arrayLen(zcu) != expect_len) return .{ .conflict = .{
  33221                         .peer_idx_a = first_idx,
  33222                         .peer_idx_b = i,
  33223                     } };
  33224                 } else {
  33225                     len = ty.arrayLen(zcu);
  33226                     first_idx = i;
  33227                 }
  33228 
  33229                 ty_ptr.* = ty.childType(zcu);
  33230                 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR
  33231             }
  33232 
  33233             const child_ty = switch (try sema.resolvePeerTypesInner(
  33234                 block,
  33235                 src,
  33236                 peer_tys,
  33237                 peer_vals,
  33238             )) {
  33239                 .success => |ty| ty,
  33240                 else => |result| return result,
  33241             };
  33242 
  33243             return .{ .success = try pt.vectorType(.{
  33244                 .len = @intCast(len.?),
  33245                 .child = child_ty.toIntern(),
  33246             }) };
  33247         },
  33248 
  33249         .c_ptr => {
  33250             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  33251             var first_idx: usize = undefined;
  33252             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  33253                 const ty = opt_ty orelse continue;
  33254                 switch (ty.zigTypeTag(zcu)) {
  33255                     .comptime_int => continue, // comptime-known integers can always coerce to C pointers
  33256                     .int => {
  33257                         if (opt_val != null) {
  33258                             // Always allow the coercion for comptime-known ints
  33259                             continue;
  33260                         } else {
  33261                             // Runtime-known, so check if the type is no bigger than a usize
  33262                             const ptr_bits = target.ptrBitWidth();
  33263                             const bits = ty.intInfo(zcu).bits;
  33264                             if (bits <= ptr_bits) continue;
  33265                         }
  33266                     },
  33267                     .null => continue,
  33268                     else => {},
  33269                 }
  33270 
  33271                 if (!ty.isPtrAtRuntime(zcu)) return .{ .conflict = .{
  33272                     .peer_idx_a = strat_reason,
  33273                     .peer_idx_b = i,
  33274                 } };
  33275 
  33276                 // Goes through optionals
  33277                 const peer_info = ty.ptrInfo(zcu);
  33278 
  33279                 var ptr_info = opt_ptr_info orelse {
  33280                     opt_ptr_info = peer_info;
  33281                     opt_ptr_info.?.flags.size = .c;
  33282                     first_idx = i;
  33283                     continue;
  33284                 };
  33285 
  33286                 // Try peer -> cur, then cur -> peer
  33287                 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) orelse {
  33288                     return .{ .conflict = .{
  33289                         .peer_idx_a = first_idx,
  33290                         .peer_idx_b = i,
  33291                     } };
  33292                 }).toIntern();
  33293 
  33294                 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) {
  33295                     const peer_sent = try ip.getCoerced(sema.gpa, pt.tid, ptr_info.sentinel, ptr_info.child);
  33296                     const ptr_sent = try ip.getCoerced(sema.gpa, pt.tid, peer_info.sentinel, ptr_info.child);
  33297                     if (ptr_sent == peer_sent) {
  33298                         ptr_info.sentinel = ptr_sent;
  33299                     } else {
  33300                         ptr_info.sentinel = .none;
  33301                     }
  33302                 } else {
  33303                     ptr_info.sentinel = .none;
  33304                 }
  33305 
  33306                 // Note that the align can be always non-zero; Zcu.ptrType will canonicalize it
  33307                 ptr_info.flags.alignment = InternPool.Alignment.min(
  33308                     if (ptr_info.flags.alignment != .none)
  33309                         ptr_info.flags.alignment
  33310                     else
  33311                         Type.fromInterned(ptr_info.child).abiAlignment(zcu),
  33312 
  33313                     if (peer_info.flags.alignment != .none)
  33314                         peer_info.flags.alignment
  33315                     else
  33316                         Type.fromInterned(peer_info.child).abiAlignment(zcu),
  33317                 );
  33318                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  33319                     return .{ .conflict = .{
  33320                         .peer_idx_a = first_idx,
  33321                         .peer_idx_b = i,
  33322                     } };
  33323                 }
  33324 
  33325                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  33326                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  33327                 {
  33328                     return .{ .conflict = .{
  33329                         .peer_idx_a = first_idx,
  33330                         .peer_idx_b = i,
  33331                     } };
  33332                 }
  33333 
  33334                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  33335                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  33336 
  33337                 opt_ptr_info = ptr_info;
  33338             }
  33339             return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) };
  33340         },
  33341 
  33342         .ptr => {
  33343             // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only
  33344             // if there were no actual slices. Else, we want the slice index to report a conflict.
  33345             var opt_slice_idx: ?usize = null;
  33346 
  33347             var any_abi_aligned = false;
  33348             var opt_ptr_info: ?InternPool.Key.PtrType = null;
  33349             var first_idx: usize = undefined;
  33350             var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error
  33351 
  33352             for (peer_tys, 0..) |opt_ty, i| {
  33353                 const ty = opt_ty orelse continue;
  33354                 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(zcu)) {
  33355                     .pointer => ty.ptrInfo(zcu),
  33356                     .@"fn" => .{
  33357                         .child = ty.toIntern(),
  33358                         .flags = .{
  33359                             .address_space = target_util.defaultAddressSpace(target, .global_constant),
  33360                         },
  33361                     },
  33362                     else => return .{ .conflict = .{
  33363                         .peer_idx_a = strat_reason,
  33364                         .peer_idx_b = i,
  33365                     } },
  33366                 };
  33367 
  33368                 switch (peer_info.flags.size) {
  33369                     .one, .many => {},
  33370                     .slice => opt_slice_idx = i,
  33371                     .c => return .{ .conflict = .{
  33372                         .peer_idx_a = strat_reason,
  33373                         .peer_idx_b = i,
  33374                     } },
  33375                 }
  33376 
  33377                 var ptr_info = opt_ptr_info orelse {
  33378                     opt_ptr_info = peer_info;
  33379                     first_idx = i;
  33380                     continue;
  33381                 };
  33382 
  33383                 other_idx = i;
  33384 
  33385                 // We want to return this in a lot of cases, so alias it here for convenience
  33386                 const generic_err: PeerResolveResult = .{ .conflict = .{
  33387                     .peer_idx_a = first_idx,
  33388                     .peer_idx_b = i,
  33389                 } };
  33390 
  33391                 // Note that the align can be always non-zero; Type.ptr will canonicalize it
  33392                 if (peer_info.flags.alignment == .none) {
  33393                     any_abi_aligned = true;
  33394                 } else if (ptr_info.flags.alignment == .none) {
  33395                     any_abi_aligned = true;
  33396                     ptr_info.flags.alignment = peer_info.flags.alignment;
  33397                 } else {
  33398                     ptr_info.flags.alignment = ptr_info.flags.alignment.minStrict(peer_info.flags.alignment);
  33399                 }
  33400 
  33401                 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
  33402                     return generic_err;
  33403                 }
  33404 
  33405                 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
  33406                     ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
  33407                 {
  33408                     return generic_err;
  33409                 }
  33410 
  33411                 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
  33412                 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
  33413                 ptr_info.flags.is_allowzero = ptr_info.flags.is_allowzero or peer_info.flags.is_allowzero;
  33414 
  33415                 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) {
  33416                     .one => switch (ip.indexToKey(peer_info.child)) {
  33417                         .array_type => |array_type| array_type.sentinel,
  33418                         else => .none,
  33419                     },
  33420                     .many, .slice => peer_info.sentinel,
  33421                     .c => unreachable,
  33422                 };
  33423 
  33424                 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) {
  33425                     .one => switch (ip.indexToKey(ptr_info.child)) {
  33426                         .array_type => |array_type| array_type.sentinel,
  33427                         else => .none,
  33428                     },
  33429                     .many, .slice => ptr_info.sentinel,
  33430                     .c => unreachable,
  33431                 };
  33432 
  33433                 // We abstract array handling slightly so that tuple pointers can work like array pointers
  33434                 const peer_pointee_array = sema.typeIsArrayLike(.fromInterned(peer_info.child));
  33435                 const cur_pointee_array = sema.typeIsArrayLike(.fromInterned(ptr_info.child));
  33436 
  33437                 // This switch is just responsible for deciding the size and pointee (not including
  33438                 // single-pointer array sentinel).
  33439                 good: {
  33440                     switch (peer_info.flags.size) {
  33441                         .one => switch (ptr_info.flags.size) {
  33442                             .one => {
  33443                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
  33444                                     ptr_info.child = pointee.toIntern();
  33445                                     break :good;
  33446                                 }
  33447 
  33448                                 const cur_arr = cur_pointee_array orelse return generic_err;
  33449                                 const peer_arr = peer_pointee_array orelse return generic_err;
  33450 
  33451                                 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| {
  33452                                     // *[n:x]T + *[n:y]T = *[n]T
  33453                                     if (cur_arr.len == peer_arr.len) {
  33454                                         ptr_info.child = (try pt.arrayType(.{
  33455                                             .len = cur_arr.len,
  33456                                             .child = elem_ty.toIntern(),
  33457                                         })).toIntern();
  33458                                         break :good;
  33459                                     }
  33460                                     // *[a]T + *[b]T = []T
  33461                                     ptr_info.flags.size = .slice;
  33462                                     ptr_info.child = elem_ty.toIntern();
  33463                                     break :good;
  33464                                 }
  33465 
  33466                                 if (peer_arr.elem_ty.toIntern() == .noreturn_type) {
  33467                                     // *struct{} + *[a]T = []T
  33468                                     ptr_info.flags.size = .slice;
  33469                                     ptr_info.child = cur_arr.elem_ty.toIntern();
  33470                                     break :good;
  33471                                 }
  33472 
  33473                                 if (cur_arr.elem_ty.toIntern() == .noreturn_type) {
  33474                                     // *[a]T + *struct{} = []T
  33475                                     ptr_info.flags.size = .slice;
  33476                                     ptr_info.child = peer_arr.elem_ty.toIntern();
  33477                                     break :good;
  33478                                 }
  33479 
  33480                                 return generic_err;
  33481                             },
  33482                             .many => {
  33483                                 // Only works for *[n]T + [*]T -> [*]T
  33484                                 const arr = peer_pointee_array orelse return generic_err;
  33485                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| {
  33486                                     ptr_info.child = pointee.toIntern();
  33487                                     break :good;
  33488                                 }
  33489                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33490                                     // *struct{} + [*]T -> [*]T
  33491                                     break :good;
  33492                                 }
  33493                                 return generic_err;
  33494                             },
  33495                             .slice => {
  33496                                 // Only works for *[n]T + []T -> []T
  33497                                 const arr = peer_pointee_array orelse return generic_err;
  33498                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| {
  33499                                     ptr_info.child = pointee.toIntern();
  33500                                     break :good;
  33501                                 }
  33502                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33503                                     // *struct{} + []T -> []T
  33504                                     break :good;
  33505                                 }
  33506                                 return generic_err;
  33507                             },
  33508                             .c => unreachable,
  33509                         },
  33510                         .many => switch (ptr_info.flags.size) {
  33511                             .one => {
  33512                                 // Only works for [*]T + *[n]T -> [*]T
  33513                                 const arr = cur_pointee_array orelse return generic_err;
  33514                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| {
  33515                                     ptr_info.flags.size = .many;
  33516                                     ptr_info.child = pointee.toIntern();
  33517                                     break :good;
  33518                                 }
  33519                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33520                                     // [*]T + *struct{} -> [*]T
  33521                                     ptr_info.flags.size = .many;
  33522                                     ptr_info.child = peer_info.child;
  33523                                     break :good;
  33524                                 }
  33525                                 return generic_err;
  33526                             },
  33527                             .many => {
  33528                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
  33529                                     ptr_info.child = pointee.toIntern();
  33530                                     break :good;
  33531                                 }
  33532                                 return generic_err;
  33533                             },
  33534                             .slice => {
  33535                                 // Only works if no peers are actually slices
  33536                                 if (opt_slice_idx) |slice_idx| {
  33537                                     return .{ .conflict = .{
  33538                                         .peer_idx_a = slice_idx,
  33539                                         .peer_idx_b = i,
  33540                                     } };
  33541                                 }
  33542                                 // Okay, then works for [*]T + "[]T" -> [*]T
  33543                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
  33544                                     ptr_info.flags.size = .many;
  33545                                     ptr_info.child = pointee.toIntern();
  33546                                     break :good;
  33547                                 }
  33548                                 return generic_err;
  33549                             },
  33550                             .c => unreachable,
  33551                         },
  33552                         .slice => switch (ptr_info.flags.size) {
  33553                             .one => {
  33554                                 // Only works for []T + *[n]T -> []T
  33555                                 const arr = cur_pointee_array orelse return generic_err;
  33556                                 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| {
  33557                                     ptr_info.flags.size = .slice;
  33558                                     ptr_info.child = pointee.toIntern();
  33559                                     break :good;
  33560                                 }
  33561                                 if (arr.elem_ty.toIntern() == .noreturn_type) {
  33562                                     // []T + *struct{} -> []T
  33563                                     ptr_info.flags.size = .slice;
  33564                                     ptr_info.child = peer_info.child;
  33565                                     break :good;
  33566                                 }
  33567                                 return generic_err;
  33568                             },
  33569                             .many => {
  33570                                 // Impossible! (current peer is an actual slice)
  33571                                 return generic_err;
  33572                             },
  33573                             .slice => {
  33574                                 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
  33575                                     ptr_info.child = pointee.toIntern();
  33576                                     break :good;
  33577                                 }
  33578                                 return generic_err;
  33579                             },
  33580                             .c => unreachable,
  33581                         },
  33582                         .c => unreachable,
  33583                     }
  33584                 }
  33585 
  33586                 const sentinel_ty = switch (ptr_info.flags.size) {
  33587                     .one => switch (ip.indexToKey(ptr_info.child)) {
  33588                         .array_type => |array_type| array_type.child,
  33589                         else => ptr_info.child,
  33590                     },
  33591                     .many, .slice, .c => ptr_info.child,
  33592                 };
  33593 
  33594                 sentinel: {
  33595                     no_sentinel: {
  33596                         if (peer_sentinel == .none) break :no_sentinel;
  33597                         if (cur_sentinel == .none) break :no_sentinel;
  33598                         const peer_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, peer_sentinel, sentinel_ty);
  33599                         const cur_sent_coerced = try ip.getCoerced(sema.gpa, pt.tid, cur_sentinel, sentinel_ty);
  33600                         if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel;
  33601                         // Sentinels match
  33602                         if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) {
  33603                             .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{
  33604                                 .len = array_type.len,
  33605                                 .child = array_type.child,
  33606                                 .sentinel = cur_sent_coerced,
  33607                             })).toIntern(),
  33608                             else => unreachable,
  33609                         } else {
  33610                             ptr_info.sentinel = cur_sent_coerced;
  33611                         }
  33612                         break :sentinel;
  33613                     }
  33614                     // Clear existing sentinel
  33615                     ptr_info.sentinel = .none;
  33616                     if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) {
  33617                         .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{
  33618                             .len = array_type.len,
  33619                             .child = array_type.child,
  33620                             .sentinel = .none,
  33621                         })).toIntern(),
  33622                         else => {},
  33623                     };
  33624                 }
  33625 
  33626                 opt_ptr_info = ptr_info;
  33627             }
  33628 
  33629             // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance)
  33630             // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to
  33631             // coerce the empty struct to a specific type, but no peer provided one. We need to
  33632             // detect this case and emit an error.
  33633             const pointee = opt_ptr_info.?.child;
  33634             switch (pointee) {
  33635                 .noreturn_type => return .{ .conflict = .{
  33636                     .peer_idx_a = first_idx,
  33637                     .peer_idx_b = other_idx,
  33638                 } },
  33639                 else => switch (ip.indexToKey(pointee)) {
  33640                     .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{
  33641                         .peer_idx_a = first_idx,
  33642                         .peer_idx_b = other_idx,
  33643                     } },
  33644                     else => {},
  33645                 },
  33646             }
  33647 
  33648             if (any_abi_aligned and opt_ptr_info.?.flags.alignment != .none) {
  33649                 opt_ptr_info.?.flags.alignment = opt_ptr_info.?.flags.alignment.minStrict(
  33650                     try Type.fromInterned(pointee).abiAlignmentSema(pt),
  33651                 );
  33652             }
  33653 
  33654             return .{ .success = try pt.ptrTypeSema(opt_ptr_info.?) };
  33655         },
  33656 
  33657         .func => {
  33658             var opt_cur_ty: ?Type = null;
  33659             var first_idx: usize = undefined;
  33660             for (peer_tys, 0..) |opt_ty, i| {
  33661                 const ty = opt_ty orelse continue;
  33662                 const cur_ty = opt_cur_ty orelse {
  33663                     opt_cur_ty = ty;
  33664                     first_idx = i;
  33665                     continue;
  33666                 };
  33667                 if (ty.zigTypeTag(zcu) != .@"fn") return .{ .conflict = .{
  33668                     .peer_idx_a = strat_reason,
  33669                     .peer_idx_b = i,
  33670                 } };
  33671                 // ty -> cur_ty
  33672                 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, false, target, src, src)) {
  33673                     continue;
  33674                 }
  33675                 // cur_ty -> ty
  33676                 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, false, target, src, src)) {
  33677                     opt_cur_ty = ty;
  33678                     continue;
  33679                 }
  33680                 return .{ .conflict = .{
  33681                     .peer_idx_a = first_idx,
  33682                     .peer_idx_b = i,
  33683                 } };
  33684             }
  33685             return .{ .success = opt_cur_ty.? };
  33686         },
  33687 
  33688         .enum_or_union => {
  33689             var opt_cur_ty: ?Type = null;
  33690             // The peer index which gave the current type
  33691             var cur_ty_idx: usize = undefined;
  33692 
  33693             for (peer_tys, 0..) |opt_ty, i| {
  33694                 const ty = opt_ty orelse continue;
  33695                 switch (ty.zigTypeTag(zcu)) {
  33696                     .enum_literal, .@"enum", .@"union" => {},
  33697                     else => return .{ .conflict = .{
  33698                         .peer_idx_a = strat_reason,
  33699                         .peer_idx_b = i,
  33700                     } },
  33701                 }
  33702                 const cur_ty = opt_cur_ty orelse {
  33703                     opt_cur_ty = ty;
  33704                     cur_ty_idx = i;
  33705                     continue;
  33706                 };
  33707 
  33708                 // We want to return this in a lot of cases, so alias it here for convenience
  33709                 const generic_err: PeerResolveResult = .{ .conflict = .{
  33710                     .peer_idx_a = cur_ty_idx,
  33711                     .peer_idx_b = i,
  33712                 } };
  33713 
  33714                 switch (cur_ty.zigTypeTag(zcu)) {
  33715                     .enum_literal => {
  33716                         opt_cur_ty = ty;
  33717                         cur_ty_idx = i;
  33718                     },
  33719                     .@"enum" => switch (ty.zigTypeTag(zcu)) {
  33720                         .enum_literal => {},
  33721                         .@"enum" => {
  33722                             if (!ty.eql(cur_ty, zcu)) return generic_err;
  33723                         },
  33724                         .@"union" => {
  33725                             const tag_ty = ty.unionTagTypeHypothetical(zcu);
  33726                             if (!tag_ty.eql(cur_ty, zcu)) return generic_err;
  33727                             opt_cur_ty = ty;
  33728                             cur_ty_idx = i;
  33729                         },
  33730                         else => unreachable,
  33731                     },
  33732                     .@"union" => switch (ty.zigTypeTag(zcu)) {
  33733                         .enum_literal => {},
  33734                         .@"enum" => {
  33735                             const cur_tag_ty = cur_ty.unionTagTypeHypothetical(zcu);
  33736                             if (!ty.eql(cur_tag_ty, zcu)) return generic_err;
  33737                         },
  33738                         .@"union" => {
  33739                             if (!ty.eql(cur_ty, zcu)) return generic_err;
  33740                         },
  33741                         else => unreachable,
  33742                     },
  33743                     else => unreachable,
  33744                 }
  33745             }
  33746             return .{ .success = opt_cur_ty.? };
  33747         },
  33748 
  33749         .comptime_int => {
  33750             for (peer_tys, 0..) |opt_ty, i| {
  33751                 const ty = opt_ty orelse continue;
  33752                 switch (ty.zigTypeTag(zcu)) {
  33753                     .comptime_int => {},
  33754                     else => return .{ .conflict = .{
  33755                         .peer_idx_a = strat_reason,
  33756                         .peer_idx_b = i,
  33757                     } },
  33758                 }
  33759             }
  33760             return .{ .success = .comptime_int };
  33761         },
  33762 
  33763         .comptime_float => {
  33764             for (peer_tys, 0..) |opt_ty, i| {
  33765                 const ty = opt_ty orelse continue;
  33766                 switch (ty.zigTypeTag(zcu)) {
  33767                     .comptime_int, .comptime_float => {},
  33768                     else => return .{ .conflict = .{
  33769                         .peer_idx_a = strat_reason,
  33770                         .peer_idx_b = i,
  33771                     } },
  33772                 }
  33773             }
  33774             return .{ .success = .comptime_float };
  33775         },
  33776 
  33777         .fixed_int => {
  33778             var idx_unsigned: ?usize = null;
  33779             var idx_signed: ?usize = null;
  33780 
  33781             // TODO: this is for compatibility with legacy behavior. See beneath the loop.
  33782             var any_comptime_known = false;
  33783 
  33784             for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| {
  33785                 const ty = opt_ty orelse continue;
  33786                 const opt_val = ptr_opt_val.*;
  33787 
  33788                 const peer_tag = ty.zigTypeTag(zcu);
  33789                 switch (peer_tag) {
  33790                     .comptime_int => {
  33791                         // If the value is undefined, we can't refine to a fixed-width int
  33792                         if (opt_val == null or opt_val.?.isUndef(zcu)) return .{ .conflict = .{
  33793                             .peer_idx_a = strat_reason,
  33794                             .peer_idx_b = i,
  33795                         } };
  33796                         any_comptime_known = true;
  33797                         ptr_opt_val.* = try sema.resolveLazyValue(opt_val.?);
  33798                         continue;
  33799                     },
  33800                     .int => {},
  33801                     else => return .{ .conflict = .{
  33802                         .peer_idx_a = strat_reason,
  33803                         .peer_idx_b = i,
  33804                     } },
  33805                 }
  33806 
  33807                 if (opt_val != null) any_comptime_known = true;
  33808 
  33809                 const info = ty.intInfo(zcu);
  33810 
  33811                 const idx_ptr = switch (info.signedness) {
  33812                     .unsigned => &idx_unsigned,
  33813                     .signed => &idx_signed,
  33814                 };
  33815 
  33816                 const largest_idx = idx_ptr.* orelse {
  33817                     idx_ptr.* = i;
  33818                     continue;
  33819                 };
  33820 
  33821                 const cur_info = peer_tys[largest_idx].?.intInfo(zcu);
  33822                 if (info.bits > cur_info.bits) {
  33823                     idx_ptr.* = i;
  33824                 }
  33825             }
  33826 
  33827             if (idx_signed == null) {
  33828                 return .{ .success = peer_tys[idx_unsigned.?].? };
  33829             }
  33830 
  33831             if (idx_unsigned == null) {
  33832                 return .{ .success = peer_tys[idx_signed.?].? };
  33833             }
  33834 
  33835             const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(zcu);
  33836             const signed_info = peer_tys[idx_signed.?].?.intInfo(zcu);
  33837             if (signed_info.bits > unsigned_info.bits) {
  33838                 return .{ .success = peer_tys[idx_signed.?].? };
  33839             }
  33840 
  33841             // TODO: this is for compatibility with legacy behavior. Before this version of PTR was
  33842             // implemented, the algorithm very often returned false positives, with the expectation
  33843             // that you'd just hit a coercion error later. One of these was that for integers, the
  33844             // largest type would always be returned, even if it couldn't fit everything. This had
  33845             // an unintentional consequence to semantics, which is that if values were known at
  33846             // comptime, they would be coerced down to the smallest type where possible. This
  33847             // behavior is unintuitive and order-dependent, so in my opinion should be eliminated,
  33848             // but for now we'll retain compatibility.
  33849             if (any_comptime_known) {
  33850                 if (unsigned_info.bits > signed_info.bits) {
  33851                     return .{ .success = peer_tys[idx_unsigned.?].? };
  33852                 }
  33853                 const idx = @min(idx_unsigned.?, idx_signed.?);
  33854                 return .{ .success = peer_tys[idx].? };
  33855             }
  33856 
  33857             return .{ .conflict = .{
  33858                 .peer_idx_a = idx_unsigned.?,
  33859                 .peer_idx_b = idx_signed.?,
  33860             } };
  33861         },
  33862 
  33863         .fixed_float => {
  33864             var opt_cur_ty: ?Type = null;
  33865 
  33866             for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
  33867                 const ty = opt_ty orelse continue;
  33868                 switch (ty.zigTypeTag(zcu)) {
  33869                     .comptime_float, .comptime_int => {},
  33870                     .int => {
  33871                         if (opt_val == null) return .{ .conflict = .{
  33872                             .peer_idx_a = strat_reason,
  33873                             .peer_idx_b = i,
  33874                         } };
  33875                     },
  33876                     .float => {
  33877                         if (opt_cur_ty) |cur_ty| {
  33878                             if (cur_ty.eql(ty, zcu)) continue;
  33879                             // Recreate the type so we eliminate any c_longdouble
  33880                             const bits = @max(cur_ty.floatBits(target), ty.floatBits(target));
  33881                             opt_cur_ty = switch (bits) {
  33882                                 16 => .f16,
  33883                                 32 => .f32,
  33884                                 64 => .f64,
  33885                                 80 => .f80,
  33886                                 128 => .f128,
  33887                                 else => unreachable,
  33888                             };
  33889                         } else {
  33890                             opt_cur_ty = ty;
  33891                         }
  33892                     },
  33893                     else => return .{ .conflict = .{
  33894                         .peer_idx_a = strat_reason,
  33895                         .peer_idx_b = i,
  33896                     } },
  33897                 }
  33898             }
  33899 
  33900             // Note that fixed_float is only chosen if there is at least one fixed-width float peer,
  33901             // so opt_cur_ty must be non-null.
  33902             return .{ .success = opt_cur_ty.? };
  33903         },
  33904 
  33905         .tuple => {
  33906             // First, check that every peer has the same approximate structure (field count)
  33907 
  33908             var opt_first_idx: ?usize = null;
  33909             var is_tuple: bool = undefined;
  33910             var field_count: usize = undefined;
  33911 
  33912             for (peer_tys, 0..) |opt_ty, i| {
  33913                 const ty = opt_ty orelse continue;
  33914 
  33915                 if (!ty.isTuple(zcu)) {
  33916                     return .{ .conflict = .{
  33917                         .peer_idx_a = strat_reason,
  33918                         .peer_idx_b = i,
  33919                     } };
  33920                 }
  33921 
  33922                 const first_idx = opt_first_idx orelse {
  33923                     opt_first_idx = i;
  33924                     is_tuple = ty.isTuple(zcu);
  33925                     field_count = ty.structFieldCount(zcu);
  33926                     continue;
  33927                 };
  33928 
  33929                 if (ty.structFieldCount(zcu) != field_count) {
  33930                     return .{ .conflict = .{
  33931                         .peer_idx_a = first_idx,
  33932                         .peer_idx_b = i,
  33933                     } };
  33934                 }
  33935             }
  33936 
  33937             assert(opt_first_idx != null);
  33938 
  33939             // Now, we'll recursively resolve the field types
  33940             const field_types = try sema.arena.alloc(InternPool.Index, field_count);
  33941             // Values for `comptime` fields - `.none` used for non-comptime fields
  33942             const field_vals = try sema.arena.alloc(InternPool.Index, field_count);
  33943             const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len);
  33944             const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len);
  33945 
  33946             for (field_types, field_vals, 0..) |*field_ty, *field_val, field_index| {
  33947                 // Fill buffers with types and values of the field
  33948                 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| {
  33949                     const ty = opt_ty orelse {
  33950                         peer_field_ty.* = null;
  33951                         peer_field_val.* = null;
  33952                         continue;
  33953                     };
  33954                     peer_field_ty.* = ty.fieldType(field_index, zcu);
  33955                     peer_field_val.* = if (opt_val) |val| try val.fieldValue(pt, field_index) else null;
  33956                 }
  33957 
  33958                 // Resolve field type recursively
  33959                 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) {
  33960                     .success => |ty| ty.toIntern(),
  33961                     else => |result| {
  33962                         const result_buf = try sema.arena.create(PeerResolveResult);
  33963                         result_buf.* = result;
  33964                         const field_name = try ip.getOrPutStringFmt(sema.gpa, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
  33965 
  33966                         // The error info needs the field types, but we can't reuse sub_peer_tys
  33967                         // since the recursive call may have clobbered it.
  33968                         const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len);
  33969                         for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| {
  33970                             // Already-resolved types won't be referenced by the error so it's fine
  33971                             // to leave them undefined.
  33972                             const ty = opt_ty orelse continue;
  33973                             peer_field_ty.* = ty.fieldType(field_index, zcu);
  33974                         }
  33975 
  33976                         return .{ .field_error = .{
  33977                             .field_name = field_name,
  33978                             .field_types = peer_field_tys,
  33979                             .sub_result = result_buf,
  33980                         } };
  33981                     },
  33982                 };
  33983 
  33984                 // Decide if this is a comptime field. If it is comptime in all peers, and the
  33985                 // coerced comptime values are all the same, we say it is comptime, else not.
  33986 
  33987                 var comptime_val: ?Value = null;
  33988                 for (peer_tys) |opt_ty| {
  33989                     const struct_ty = opt_ty orelse continue;
  33990                     try struct_ty.resolveStructFieldInits(pt);
  33991 
  33992                     const uncoerced_field_val = try struct_ty.structFieldValueComptime(pt, field_index) orelse {
  33993                         comptime_val = null;
  33994                         break;
  33995                     };
  33996                     const uncoerced_field = Air.internedToRef(uncoerced_field_val.toIntern());
  33997                     const coerced_inst = sema.coerceExtra(block, .fromInterned(field_ty.*), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) {
  33998                         // 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
  33999                         error.NotCoercible => {
  34000                             comptime_val = null;
  34001                             break;
  34002                         },
  34003                         else => |e| return e,
  34004                     };
  34005                     const coerced_val = (try sema.resolveValue(coerced_inst)) orelse continue;
  34006                     const existing = comptime_val orelse {
  34007                         comptime_val = coerced_val;
  34008                         continue;
  34009                     };
  34010                     if (!coerced_val.eql(existing, .fromInterned(field_ty.*), zcu)) {
  34011                         comptime_val = null;
  34012                         break;
  34013                     }
  34014                 }
  34015 
  34016                 field_val.* = if (comptime_val) |v| v.toIntern() else .none;
  34017             }
  34018 
  34019             const final_ty = try ip.getTupleType(zcu.gpa, pt.tid, .{
  34020                 .types = field_types,
  34021                 .values = field_vals,
  34022             });
  34023 
  34024             return .{ .success = .fromInterned(final_ty) };
  34025         },
  34026 
  34027         .exact => {
  34028             var expect_ty: ?Type = null;
  34029             var first_idx: usize = undefined;
  34030             for (peer_tys, 0..) |opt_ty, i| {
  34031                 const ty = opt_ty orelse continue;
  34032                 if (expect_ty) |expect| {
  34033                     if (!ty.eql(expect, zcu)) return .{ .conflict = .{
  34034                         .peer_idx_a = first_idx,
  34035                         .peer_idx_b = i,
  34036                     } };
  34037                 } else {
  34038                     expect_ty = ty;
  34039                     first_idx = i;
  34040                 }
  34041             }
  34042             return .{ .success = expect_ty.? };
  34043         },
  34044     }
  34045 }
  34046 
  34047 fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type {
  34048     // e0 -> e1
  34049     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) {
  34050         return e1;
  34051     }
  34052 
  34053     // e1 -> e0
  34054     if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) {
  34055         return e0;
  34056     }
  34057 
  34058     return sema.errorSetMerge(e0, e1);
  34059 }
  34060 
  34061 fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type {
  34062     const target = sema.pt.zcu.getTarget();
  34063 
  34064     // ty_b -> ty_a
  34065     if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, false, target, src, src, null)) {
  34066         return ty_a;
  34067     }
  34068 
  34069     // ty_a -> ty_b
  34070     if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, false, target, src, src, null)) {
  34071         return ty_b;
  34072     }
  34073 
  34074     return null;
  34075 }
  34076 
  34077 const ArrayLike = struct {
  34078     len: u64,
  34079     /// `noreturn` indicates that this type is `struct{}` so can coerce to anything
  34080     elem_ty: Type,
  34081 };
  34082 fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike {
  34083     const pt = sema.pt;
  34084     const zcu = pt.zcu;
  34085     return switch (ty.zigTypeTag(zcu)) {
  34086         .array => .{
  34087             .len = ty.arrayLen(zcu),
  34088             .elem_ty = ty.childType(zcu),
  34089         },
  34090         .@"struct" => {
  34091             const field_count = ty.structFieldCount(zcu);
  34092             if (field_count == 0) return .{
  34093                 .len = 0,
  34094                 .elem_ty = .noreturn,
  34095             };
  34096             if (!ty.isTuple(zcu)) return null;
  34097             const elem_ty = ty.fieldType(0, zcu);
  34098             for (1..field_count) |i| {
  34099                 if (!ty.fieldType(i, zcu).eql(elem_ty, zcu)) {
  34100                     return null;
  34101                 }
  34102             }
  34103             return .{
  34104                 .len = field_count,
  34105                 .elem_ty = elem_ty,
  34106             };
  34107         },
  34108         else => null,
  34109     };
  34110 }
  34111 
  34112 pub fn resolveIes(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!void {
  34113     const pt = sema.pt;
  34114     const zcu = pt.zcu;
  34115     const ip = &zcu.intern_pool;
  34116 
  34117     if (sema.fn_ret_ty_ies) |ies| {
  34118         try sema.resolveInferredErrorSetPtr(block, src, ies);
  34119         assert(ies.resolved != .none);
  34120         ip.funcIesResolved(sema.func_index).* = ies.resolved;
  34121     }
  34122 }
  34123 
  34124 pub fn resolveFnTypes(sema: *Sema, fn_ty: Type, src: LazySrcLoc) CompileError!void {
  34125     const pt = sema.pt;
  34126     const zcu = pt.zcu;
  34127     const ip = &zcu.intern_pool;
  34128     const fn_ty_info = zcu.typeToFunc(fn_ty).?;
  34129 
  34130     try Type.fromInterned(fn_ty_info.return_type).resolveFully(pt);
  34131 
  34132     if (zcu.comp.config.any_error_tracing and
  34133         Type.fromInterned(fn_ty_info.return_type).isError(zcu))
  34134     {
  34135         // Ensure the type exists so that backends can assume that.
  34136         _ = try sema.getBuiltinType(src, .StackTrace);
  34137     }
  34138 
  34139     for (0..fn_ty_info.param_types.len) |i| {
  34140         try Type.fromInterned(fn_ty_info.param_types.get(ip)[i]).resolveFully(pt);
  34141     }
  34142 }
  34143 
  34144 fn resolveLazyValue(sema: *Sema, val: Value) CompileError!Value {
  34145     return val.resolveLazy(sema.arena, sema.pt);
  34146 }
  34147 
  34148 /// Resolve a struct's alignment only without triggering resolution of its layout.
  34149 /// Asserts that the alignment is not yet resolved and the layout is non-packed.
  34150 pub fn resolveStructAlignment(
  34151     sema: *Sema,
  34152     ty: InternPool.Index,
  34153     struct_type: InternPool.LoadedStructType,
  34154 ) SemaError!void {
  34155     const pt = sema.pt;
  34156     const zcu = pt.zcu;
  34157     const ip = &zcu.intern_pool;
  34158     const target = zcu.getTarget();
  34159 
  34160     assert(sema.owner.unwrap().type == ty);
  34161 
  34162     assert(struct_type.layout != .@"packed");
  34163     assert(struct_type.flagsUnordered(ip).alignment == .none);
  34164 
  34165     const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
  34166 
  34167     // We'll guess "pointer-aligned", if the struct has an
  34168     // underaligned pointer field then some allocations
  34169     // might require explicit alignment.
  34170     if (struct_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return;
  34171 
  34172     try sema.resolveStructFieldTypes(ty, struct_type);
  34173 
  34174     // We'll guess "pointer-aligned", if the struct has an
  34175     // underaligned pointer field then some allocations
  34176     // might require explicit alignment.
  34177     if (struct_type.assumePointerAlignedIfWip(ip, ptr_align)) return;
  34178     defer struct_type.clearAlignmentWip(ip);
  34179 
  34180     var alignment: Alignment = .@"1";
  34181 
  34182     for (0..struct_type.field_types.len) |i| {
  34183         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34184         if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt))
  34185             continue;
  34186         const field_align = try field_ty.structFieldAlignmentSema(
  34187             struct_type.fieldAlign(ip, i),
  34188             struct_type.layout,
  34189             pt,
  34190         );
  34191         alignment = alignment.maxStrict(field_align);
  34192     }
  34193 
  34194     struct_type.setAlignment(ip, alignment);
  34195 }
  34196 
  34197 pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
  34198     const pt = sema.pt;
  34199     const zcu = pt.zcu;
  34200     const ip = &zcu.intern_pool;
  34201     const struct_type = zcu.typeToStruct(ty) orelse return;
  34202 
  34203     assert(sema.owner.unwrap().type == ty.toIntern());
  34204 
  34205     if (struct_type.haveLayout(ip))
  34206         return;
  34207 
  34208     try sema.resolveStructFieldTypes(ty.toIntern(), struct_type);
  34209 
  34210     if (struct_type.layout == .@"packed") {
  34211         sema.backingIntType(struct_type) catch |err| switch (err) {
  34212             error.OutOfMemory, error.AnalysisFail => |e| return e,
  34213             error.ComptimeBreak, error.ComptimeReturn => unreachable,
  34214         };
  34215         return;
  34216     }
  34217 
  34218     if (struct_type.setLayoutWip(ip)) {
  34219         const msg = try sema.errMsg(
  34220             ty.srcLoc(zcu),
  34221             "struct '{f}' depends on itself",
  34222             .{ty.fmt(pt)},
  34223         );
  34224         return sema.failWithOwnedErrorMsg(null, msg);
  34225     }
  34226     defer struct_type.clearLayoutWip(ip);
  34227 
  34228     const aligns = try sema.arena.alloc(Alignment, struct_type.field_types.len);
  34229     const sizes = try sema.arena.alloc(u64, struct_type.field_types.len);
  34230 
  34231     var big_align: Alignment = .@"1";
  34232 
  34233     for (aligns, sizes, 0..) |*field_align, *field_size, i| {
  34234         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34235         if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) {
  34236             struct_type.offsets.get(ip)[i] = 0;
  34237             field_size.* = 0;
  34238             field_align.* = .none;
  34239             continue;
  34240         }
  34241 
  34242         field_size.* = field_ty.abiSizeSema(pt) catch |err| switch (err) {
  34243             error.AnalysisFail => {
  34244                 const msg = sema.err orelse return err;
  34245                 try sema.addFieldErrNote(ty, i, msg, "while checking this field", .{});
  34246                 return err;
  34247             },
  34248             else => return err,
  34249         };
  34250         field_align.* = try field_ty.structFieldAlignmentSema(
  34251             struct_type.fieldAlign(ip, i),
  34252             struct_type.layout,
  34253             pt,
  34254         );
  34255         big_align = big_align.maxStrict(field_align.*);
  34256     }
  34257 
  34258     if (struct_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) {
  34259         const msg = try sema.errMsg(
  34260             ty.srcLoc(zcu),
  34261             "struct layout depends on it having runtime bits",
  34262             .{},
  34263         );
  34264         return sema.failWithOwnedErrorMsg(null, msg);
  34265     }
  34266 
  34267     if (struct_type.flagsUnordered(ip).assumed_pointer_aligned and
  34268         big_align.compareStrict(.neq, Alignment.fromByteUnits(@divExact(zcu.getTarget().ptrBitWidth(), 8))))
  34269     {
  34270         const msg = try sema.errMsg(
  34271             ty.srcLoc(zcu),
  34272             "struct layout depends on being pointer aligned",
  34273             .{},
  34274         );
  34275         return sema.failWithOwnedErrorMsg(null, msg);
  34276     }
  34277 
  34278     if (struct_type.hasReorderedFields()) {
  34279         const runtime_order = struct_type.runtime_order.get(ip);
  34280 
  34281         for (runtime_order, 0..) |*ro, i| {
  34282             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34283             if (struct_type.fieldIsComptime(ip, i) or try field_ty.comptimeOnlySema(pt)) {
  34284                 ro.* = .omitted;
  34285             } else {
  34286                 ro.* = @enumFromInt(i);
  34287             }
  34288         }
  34289 
  34290         const RuntimeOrder = InternPool.LoadedStructType.RuntimeOrder;
  34291 
  34292         const AlignSortContext = struct {
  34293             aligns: []const Alignment,
  34294 
  34295             fn lessThan(ctx: @This(), a: RuntimeOrder, b: RuntimeOrder) bool {
  34296                 if (a == .omitted) return false;
  34297                 if (b == .omitted) return true;
  34298                 const a_align = ctx.aligns[@intFromEnum(a)];
  34299                 const b_align = ctx.aligns[@intFromEnum(b)];
  34300                 return a_align.compare(.gt, b_align);
  34301             }
  34302         };
  34303         if (!zcu.backendSupportsFeature(.field_reordering)) {
  34304             // TODO: we should probably also reorder tuple fields? This is a bit weird because it'll involve
  34305             // mutating the `InternPool` for a non-container type.
  34306             //
  34307             // TODO: implement field reordering support in all the backends!
  34308             //
  34309             // This logic does not reorder fields; it only moves the omitted ones to the end
  34310             // so that logic elsewhere does not need to special-case here.
  34311             var i: usize = 0;
  34312             var off: usize = 0;
  34313             while (i + off < runtime_order.len) {
  34314                 if (runtime_order[i + off] == .omitted) {
  34315                     off += 1;
  34316                     continue;
  34317                 }
  34318                 runtime_order[i] = runtime_order[i + off];
  34319                 i += 1;
  34320             }
  34321             @memset(runtime_order[i..], .omitted);
  34322         } else {
  34323             mem.sortUnstable(RuntimeOrder, runtime_order, AlignSortContext{
  34324                 .aligns = aligns,
  34325             }, AlignSortContext.lessThan);
  34326         }
  34327     }
  34328 
  34329     // Calculate size, alignment, and field offsets.
  34330     const offsets = struct_type.offsets.get(ip);
  34331     var it = struct_type.iterateRuntimeOrder(ip);
  34332     var offset: u64 = 0;
  34333     while (it.next()) |i| {
  34334         offsets[i] = @intCast(aligns[i].forward(offset));
  34335         offset = offsets[i] + sizes[i];
  34336     }
  34337     const size = std.math.cast(u32, big_align.forward(offset)) orelse {
  34338         const msg = try sema.errMsg(
  34339             ty.srcLoc(zcu),
  34340             "struct layout requires size {d}, this compiler implementation supports up to {d}",
  34341             .{ big_align.forward(offset), std.math.maxInt(u32) },
  34342         );
  34343         return sema.failWithOwnedErrorMsg(null, msg);
  34344     };
  34345     struct_type.setLayoutResolved(ip, size, big_align);
  34346     _ = try ty.comptimeOnlySema(pt);
  34347 }
  34348 
  34349 fn backingIntType(
  34350     sema: *Sema,
  34351     struct_type: InternPool.LoadedStructType,
  34352 ) CompileError!void {
  34353     const pt = sema.pt;
  34354     const zcu = pt.zcu;
  34355     const gpa = zcu.gpa;
  34356     const ip = &zcu.intern_pool;
  34357 
  34358     var analysis_arena = std.heap.ArenaAllocator.init(gpa);
  34359     defer analysis_arena.deinit();
  34360 
  34361     var block: Block = .{
  34362         .parent = null,
  34363         .sema = sema,
  34364         .namespace = struct_type.namespace,
  34365         .instructions = .{},
  34366         .inlining = null,
  34367         .comptime_reason = null, // set below if needed
  34368         .src_base_inst = struct_type.zir_index,
  34369         .type_name_ctx = struct_type.name,
  34370     };
  34371     defer assert(block.instructions.items.len == 0);
  34372 
  34373     const fields_bit_sum = blk: {
  34374         var accumulator: u64 = 0;
  34375         for (0..struct_type.field_types.len) |i| {
  34376             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34377             accumulator += try field_ty.bitSizeSema(pt);
  34378         }
  34379         break :blk accumulator;
  34380     };
  34381 
  34382     const zir = zcu.namespacePtr(struct_type.namespace).fileScope(zcu).zir.?;
  34383     const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
  34384     const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
  34385     assert(extended.opcode == .struct_decl);
  34386     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
  34387 
  34388     if (small.has_backing_int) {
  34389         var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
  34390         const captures_len = if (small.has_captures_len) blk: {
  34391             const captures_len = zir.extra[extra_index];
  34392             extra_index += 1;
  34393             break :blk captures_len;
  34394         } else 0;
  34395         extra_index += @intFromBool(small.has_fields_len);
  34396         extra_index += @intFromBool(small.has_decls_len);
  34397 
  34398         extra_index += captures_len * 2;
  34399 
  34400         const backing_int_body_len = zir.extra[extra_index];
  34401         extra_index += 1;
  34402 
  34403         const backing_int_src: LazySrcLoc = .{
  34404             .base_node_inst = struct_type.zir_index,
  34405             .offset = .{ .node_offset_container_tag = .zero },
  34406         };
  34407         block.comptime_reason = .{ .reason = .{
  34408             .src = backing_int_src,
  34409             .r = .{ .simple = .type },
  34410         } };
  34411         const backing_int_ty = blk: {
  34412             if (backing_int_body_len == 0) {
  34413                 const backing_int_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  34414                 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
  34415             } else {
  34416                 const body = zir.bodySlice(extra_index, backing_int_body_len);
  34417                 const ty_ref = try sema.resolveInlineBody(&block, body, zir_index);
  34418                 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
  34419             }
  34420         };
  34421 
  34422         try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum);
  34423         struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
  34424     } else {
  34425         if (fields_bit_sum > std.math.maxInt(u16)) {
  34426             return sema.fail(&block, block.nodeOffset(.zero), "size of packed struct '{d}' exceeds maximum bit width of 65535", .{fields_bit_sum});
  34427         }
  34428         const backing_int_ty = try pt.intType(.unsigned, @intCast(fields_bit_sum));
  34429         struct_type.setBackingIntType(ip, backing_int_ty.toIntern());
  34430     }
  34431 
  34432     try sema.flushExports();
  34433 }
  34434 
  34435 fn checkBackingIntType(sema: *Sema, block: *Block, src: LazySrcLoc, backing_int_ty: Type, fields_bit_sum: u64) CompileError!void {
  34436     const pt = sema.pt;
  34437     const zcu = pt.zcu;
  34438 
  34439     if (!backing_int_ty.isInt(zcu)) {
  34440         return sema.fail(block, src, "expected backing integer type, found '{f}'", .{backing_int_ty.fmt(pt)});
  34441     }
  34442     if (backing_int_ty.bitSize(zcu) != fields_bit_sum) {
  34443         return sema.fail(
  34444             block,
  34445             src,
  34446             "backing integer type '{f}' has bit size {d} but the struct fields have a total bit size of {d}",
  34447             .{ backing_int_ty.fmt(pt), backing_int_ty.bitSize(zcu), fields_bit_sum },
  34448         );
  34449     }
  34450 }
  34451 
  34452 fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  34453     const pt = sema.pt;
  34454     if (!ty.isIndexable(pt.zcu)) {
  34455         const msg = msg: {
  34456             const msg = try sema.errMsg(src, "type '{f}' does not support indexing", .{ty.fmt(pt)});
  34457             errdefer msg.destroy(sema.gpa);
  34458             try sema.errNote(src, msg, "operand must be an array, slice, tuple, or vector", .{});
  34459             break :msg msg;
  34460         };
  34461         return sema.failWithOwnedErrorMsg(block, msg);
  34462     }
  34463 }
  34464 
  34465 fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
  34466     const pt = sema.pt;
  34467     const zcu = pt.zcu;
  34468     if (ty.zigTypeTag(zcu) == .pointer) {
  34469         switch (ty.ptrSize(zcu)) {
  34470             .slice, .many, .c => return,
  34471             .one => {
  34472                 const elem_ty = ty.childType(zcu);
  34473                 if (elem_ty.zigTypeTag(zcu) == .array) return;
  34474                 // TODO https://github.com/ziglang/zig/issues/15479
  34475                 // if (elem_ty.isTuple()) return;
  34476             },
  34477         }
  34478     }
  34479     const msg = msg: {
  34480         const msg = try sema.errMsg(src, "type '{f}' is not an indexable pointer", .{ty.fmt(pt)});
  34481         errdefer msg.destroy(sema.gpa);
  34482         try sema.errNote(src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{});
  34483         break :msg msg;
  34484     };
  34485     return sema.failWithOwnedErrorMsg(block, msg);
  34486 }
  34487 
  34488 /// Resolve a unions's alignment only without triggering resolution of its layout.
  34489 /// Asserts that the alignment is not yet resolved.
  34490 pub fn resolveUnionAlignment(
  34491     sema: *Sema,
  34492     ty: Type,
  34493     union_type: InternPool.LoadedUnionType,
  34494 ) SemaError!void {
  34495     const pt = sema.pt;
  34496     const zcu = pt.zcu;
  34497     const ip = &zcu.intern_pool;
  34498     const target = zcu.getTarget();
  34499 
  34500     assert(sema.owner.unwrap().type == ty.toIntern());
  34501 
  34502     assert(!union_type.haveLayout(ip));
  34503 
  34504     const ptr_align = Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
  34505 
  34506     // We'll guess "pointer-aligned", if the union has an
  34507     // underaligned pointer field then some allocations
  34508     // might require explicit alignment.
  34509     if (union_type.assumePointerAlignedIfFieldTypesWip(ip, ptr_align)) return;
  34510 
  34511     try sema.resolveUnionFieldTypes(ty, union_type);
  34512 
  34513     var max_align: Alignment = .@"1";
  34514     for (0..union_type.field_types.len) |field_index| {
  34515         const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
  34516         if (!(try field_ty.hasRuntimeBitsSema(pt))) continue;
  34517 
  34518         const explicit_align = union_type.fieldAlign(ip, field_index);
  34519         const field_align = if (explicit_align != .none)
  34520             explicit_align
  34521         else
  34522             try field_ty.abiAlignmentSema(sema.pt);
  34523 
  34524         max_align = max_align.max(field_align);
  34525     }
  34526 
  34527     union_type.setAlignment(ip, max_align);
  34528 }
  34529 
  34530 /// This logic must be kept in sync with `Type.getUnionLayout`.
  34531 pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
  34532     const pt = sema.pt;
  34533     const ip = &pt.zcu.intern_pool;
  34534 
  34535     try sema.resolveUnionFieldTypes(ty, ip.loadUnionType(ty.ip_index));
  34536 
  34537     // Load again, since the tag type might have changed due to resolution.
  34538     const union_type = ip.loadUnionType(ty.ip_index);
  34539 
  34540     assert(sema.owner.unwrap().type == ty.toIntern());
  34541 
  34542     const old_flags = union_type.flagsUnordered(ip);
  34543     switch (old_flags.status) {
  34544         .none, .have_field_types => {},
  34545         .field_types_wip, .layout_wip => {
  34546             const msg = try sema.errMsg(
  34547                 ty.srcLoc(pt.zcu),
  34548                 "union '{f}' depends on itself",
  34549                 .{ty.fmt(pt)},
  34550             );
  34551             return sema.failWithOwnedErrorMsg(null, msg);
  34552         },
  34553         .have_layout, .fully_resolved_wip, .fully_resolved => return,
  34554     }
  34555 
  34556     errdefer union_type.setStatusIfLayoutWip(ip, old_flags.status);
  34557 
  34558     union_type.setStatus(ip, .layout_wip);
  34559 
  34560     var max_size: u64 = 0;
  34561     var max_align: Alignment = .@"1";
  34562     for (0..union_type.field_types.len) |field_index| {
  34563         const field_ty: Type = .fromInterned(union_type.field_types.get(ip)[field_index]);
  34564         if (field_ty.isNoReturn(pt.zcu)) continue;
  34565 
  34566         // We need to call `hasRuntimeBits` before calling `abiSize` to prevent reachable `unreachable`s,
  34567         // but `hasRuntimeBits` only resolves field types and so may infinite recurse on a layout wip type,
  34568         // so we must resolve the layout manually first, instead of waiting for `abiSize` to do it for us.
  34569         // This is arguably just hacking around bugs in both `abiSize` for not allowing arbitrary types to
  34570         // be queried, enabling failures to be handled with the emission of a compile error, and also in
  34571         // `hasRuntimeBits` for ever being able to infinite recurse in the first place.
  34572         try field_ty.resolveLayout(pt);
  34573 
  34574         if (try field_ty.hasRuntimeBitsSema(pt)) {
  34575             max_size = @max(max_size, field_ty.abiSizeSema(pt) catch |err| switch (err) {
  34576                 error.AnalysisFail => {
  34577                     const msg = sema.err orelse return err;
  34578                     try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{});
  34579                     return err;
  34580                 },
  34581                 else => return err,
  34582             });
  34583         }
  34584 
  34585         const explicit_align = union_type.fieldAlign(ip, field_index);
  34586         const field_align = if (explicit_align != .none)
  34587             explicit_align
  34588         else
  34589             try field_ty.abiAlignmentSema(pt);
  34590         max_align = max_align.max(field_align);
  34591     }
  34592 
  34593     const has_runtime_tag = union_type.flagsUnordered(ip).runtime_tag.hasTag() and
  34594         try Type.fromInterned(union_type.enum_tag_ty).hasRuntimeBitsSema(pt);
  34595     const size, const alignment, const padding = if (has_runtime_tag) layout: {
  34596         const enum_tag_type: Type = .fromInterned(union_type.enum_tag_ty);
  34597         const tag_align = try enum_tag_type.abiAlignmentSema(pt);
  34598         const tag_size = try enum_tag_type.abiSizeSema(pt);
  34599 
  34600         // Put the tag before or after the payload depending on which one's
  34601         // alignment is greater.
  34602         var size: u64 = 0;
  34603         var padding: u32 = 0;
  34604         if (tag_align.order(max_align).compare(.gte)) {
  34605             // {Tag, Payload}
  34606             size += tag_size;
  34607             size = max_align.forward(size);
  34608             size += max_size;
  34609             const prev_size = size;
  34610             size = tag_align.forward(size);
  34611             padding = @intCast(size - prev_size);
  34612         } else {
  34613             // {Payload, Tag}
  34614             size += max_size;
  34615             size = switch (pt.zcu.getTarget().ofmt) {
  34616                 .c => max_align,
  34617                 else => tag_align,
  34618             }.forward(size);
  34619             size += tag_size;
  34620             const prev_size = size;
  34621             size = max_align.forward(size);
  34622             padding = @intCast(size - prev_size);
  34623         }
  34624 
  34625         break :layout .{ size, max_align.max(tag_align), padding };
  34626     } else .{ max_align.forward(max_size), max_align, 0 };
  34627 
  34628     const casted_size = std.math.cast(u32, size) orelse {
  34629         const msg = try sema.errMsg(
  34630             ty.srcLoc(pt.zcu),
  34631             "union layout requires size {d}, this compiler implementation supports up to {d}",
  34632             .{ size, std.math.maxInt(u32) },
  34633         );
  34634         return sema.failWithOwnedErrorMsg(null, msg);
  34635     };
  34636     union_type.setHaveLayout(ip, casted_size, padding, alignment);
  34637 
  34638     if (union_type.flagsUnordered(ip).assumed_runtime_bits and !(try ty.hasRuntimeBitsSema(pt))) {
  34639         const msg = try sema.errMsg(
  34640             ty.srcLoc(pt.zcu),
  34641             "union layout depends on it having runtime bits",
  34642             .{},
  34643         );
  34644         return sema.failWithOwnedErrorMsg(null, msg);
  34645     }
  34646 
  34647     if (union_type.flagsUnordered(ip).assumed_pointer_aligned and
  34648         alignment.compareStrict(.neq, Alignment.fromByteUnits(@divExact(pt.zcu.getTarget().ptrBitWidth(), 8))))
  34649     {
  34650         const msg = try sema.errMsg(
  34651             ty.srcLoc(pt.zcu),
  34652             "union layout depends on being pointer aligned",
  34653             .{},
  34654         );
  34655         return sema.failWithOwnedErrorMsg(null, msg);
  34656     }
  34657     _ = try ty.comptimeOnlySema(pt);
  34658 }
  34659 
  34660 /// Returns `error.AnalysisFail` if any of the types (recursively) failed to
  34661 /// be resolved.
  34662 pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void {
  34663     try sema.resolveStructLayout(ty);
  34664     try sema.resolveStructFieldInits(ty);
  34665 
  34666     const pt = sema.pt;
  34667     const zcu = pt.zcu;
  34668     const ip = &zcu.intern_pool;
  34669     const struct_type = zcu.typeToStruct(ty).?;
  34670 
  34671     assert(sema.owner.unwrap().type == ty.toIntern());
  34672 
  34673     if (struct_type.setFullyResolved(ip)) return;
  34674     errdefer struct_type.clearFullyResolved(ip);
  34675 
  34676     // After we have resolve struct layout we have to go over the fields again to
  34677     // make sure pointer fields get their child types resolved as well.
  34678     // See also similar code for unions.
  34679 
  34680     for (0..struct_type.field_types.len) |i| {
  34681         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  34682         try field_ty.resolveFully(pt);
  34683     }
  34684 }
  34685 
  34686 pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void {
  34687     try sema.resolveUnionLayout(ty);
  34688 
  34689     const pt = sema.pt;
  34690     const zcu = pt.zcu;
  34691     const ip = &zcu.intern_pool;
  34692     const union_obj = zcu.typeToUnion(ty).?;
  34693 
  34694     assert(sema.owner.unwrap().type == ty.toIntern());
  34695 
  34696     switch (union_obj.flagsUnordered(ip).status) {
  34697         .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
  34698         .fully_resolved_wip, .fully_resolved => return,
  34699     }
  34700 
  34701     {
  34702         // After we have resolve union layout we have to go over the fields again to
  34703         // make sure pointer fields get their child types resolved as well.
  34704         // See also similar code for structs.
  34705         const prev_status = union_obj.flagsUnordered(ip).status;
  34706         errdefer union_obj.setStatus(ip, prev_status);
  34707 
  34708         union_obj.setStatus(ip, .fully_resolved_wip);
  34709         for (0..union_obj.field_types.len) |field_index| {
  34710             const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
  34711             try field_ty.resolveFully(pt);
  34712         }
  34713         union_obj.setStatus(ip, .fully_resolved);
  34714     }
  34715 
  34716     // And let's not forget comptime-only status.
  34717     _ = try ty.comptimeOnlySema(pt);
  34718 }
  34719 
  34720 pub fn resolveStructFieldTypes(
  34721     sema: *Sema,
  34722     ty: InternPool.Index,
  34723     struct_type: InternPool.LoadedStructType,
  34724 ) SemaError!void {
  34725     const pt = sema.pt;
  34726     const zcu = pt.zcu;
  34727     const ip = &zcu.intern_pool;
  34728 
  34729     assert(sema.owner.unwrap().type == ty);
  34730 
  34731     if (struct_type.haveFieldTypes(ip)) return;
  34732 
  34733     if (struct_type.setFieldTypesWip(ip)) {
  34734         const msg = try sema.errMsg(
  34735             Type.fromInterned(ty).srcLoc(zcu),
  34736             "struct '{f}' depends on itself",
  34737             .{Type.fromInterned(ty).fmt(pt)},
  34738         );
  34739         return sema.failWithOwnedErrorMsg(null, msg);
  34740     }
  34741     defer struct_type.clearFieldTypesWip(ip);
  34742 
  34743     sema.structFields(struct_type) catch |err| switch (err) {
  34744         error.AnalysisFail, error.OutOfMemory => |e| return e,
  34745         error.ComptimeBreak, error.ComptimeReturn => unreachable,
  34746     };
  34747 }
  34748 
  34749 pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
  34750     const pt = sema.pt;
  34751     const zcu = pt.zcu;
  34752     const ip = &zcu.intern_pool;
  34753     const struct_type = zcu.typeToStruct(ty) orelse return;
  34754 
  34755     assert(sema.owner.unwrap().type == ty.toIntern());
  34756 
  34757     // Inits can start as resolved
  34758     if (struct_type.haveFieldInits(ip)) return;
  34759 
  34760     try sema.resolveStructLayout(ty);
  34761 
  34762     if (struct_type.setInitsWip(ip)) {
  34763         const msg = try sema.errMsg(
  34764             ty.srcLoc(zcu),
  34765             "struct '{f}' depends on itself",
  34766             .{ty.fmt(pt)},
  34767         );
  34768         return sema.failWithOwnedErrorMsg(null, msg);
  34769     }
  34770     defer struct_type.clearInitsWip(ip);
  34771 
  34772     sema.structFieldInits(struct_type) catch |err| switch (err) {
  34773         error.AnalysisFail, error.OutOfMemory => |e| return e,
  34774         error.ComptimeBreak, error.ComptimeReturn => unreachable,
  34775     };
  34776     struct_type.setHaveFieldInits(ip);
  34777 }
  34778 
  34779 pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.LoadedUnionType) SemaError!void {
  34780     const pt = sema.pt;
  34781     const zcu = pt.zcu;
  34782     const ip = &zcu.intern_pool;
  34783 
  34784     assert(sema.owner.unwrap().type == ty.toIntern());
  34785 
  34786     switch (union_type.flagsUnordered(ip).status) {
  34787         .none => {},
  34788         .field_types_wip => {
  34789             const msg = try sema.errMsg(ty.srcLoc(zcu), "union '{f}' depends on itself", .{ty.fmt(pt)});
  34790             return sema.failWithOwnedErrorMsg(null, msg);
  34791         },
  34792         .have_field_types,
  34793         .have_layout,
  34794         .layout_wip,
  34795         .fully_resolved_wip,
  34796         .fully_resolved,
  34797         => return,
  34798     }
  34799 
  34800     union_type.setStatus(ip, .field_types_wip);
  34801     errdefer union_type.setStatus(ip, .none);
  34802     sema.unionFields(ty.toIntern(), union_type) catch |err| switch (err) {
  34803         error.AnalysisFail, error.OutOfMemory => |e| return e,
  34804         error.ComptimeBreak, error.ComptimeReturn => unreachable,
  34805     };
  34806     union_type.setStatus(ip, .have_field_types);
  34807 }
  34808 
  34809 /// Returns a normal error set corresponding to the fully populated inferred
  34810 /// error set.
  34811 fn resolveInferredErrorSet(
  34812     sema: *Sema,
  34813     block: *Block,
  34814     src: LazySrcLoc,
  34815     ies_index: InternPool.Index,
  34816 ) CompileError!InternPool.Index {
  34817     const pt = sema.pt;
  34818     const zcu = pt.zcu;
  34819     const ip = &zcu.intern_pool;
  34820     const func_index = ip.iesFuncIndex(ies_index);
  34821     const func = zcu.funcInfo(func_index);
  34822 
  34823     try sema.declareDependency(.{ .interned = func_index }); // resolved IES
  34824 
  34825     try zcu.maybeUnresolveIes(func_index);
  34826     const resolved_ty = func.resolvedErrorSetUnordered(ip);
  34827     if (resolved_ty != .none) return resolved_ty;
  34828 
  34829     if (zcu.analysis_in_progress.contains(AnalUnit.wrap(.{ .func = func_index }))) {
  34830         return sema.fail(block, src, "unable to resolve inferred error set", .{});
  34831     }
  34832 
  34833     // In order to ensure that all dependencies are properly added to the set,
  34834     // we need to ensure the function body is analyzed of the inferred error
  34835     // set. However, in the case of comptime/inline function calls with
  34836     // inferred error sets, each call gets an adhoc InferredErrorSet object, which
  34837     // has no corresponding function body.
  34838     const ies_func_info = zcu.typeToFunc(.fromInterned(func.ty)).?;
  34839     // if ies declared by a inline function with generic return type, the return_type should be generic_poison,
  34840     // because inline function does not create a new declaration, and the ies has been filled with analyzeCall,
  34841     // so here we can simply skip this case.
  34842     if (ies_func_info.return_type == .generic_poison_type) {
  34843         assert(ies_func_info.cc == .@"inline");
  34844     } else if (ip.errorUnionSet(ies_func_info.return_type) == ies_index) {
  34845         if (ies_func_info.is_generic) {
  34846             return sema.failWithOwnedErrorMsg(block, msg: {
  34847                 const msg = try sema.errMsg(src, "unable to resolve inferred error set of generic function", .{});
  34848                 errdefer msg.destroy(sema.gpa);
  34849                 try sema.errNote(zcu.navSrcLoc(func.owner_nav), msg, "generic function declared here", .{});
  34850                 break :msg msg;
  34851             });
  34852         }
  34853         // In this case we are dealing with the actual InferredErrorSet object that
  34854         // corresponds to the function, not one created to track an inline/comptime call.
  34855         try sema.addReferenceEntry(block, src, AnalUnit.wrap(.{ .func = func_index }));
  34856         try pt.ensureFuncBodyUpToDate(func_index);
  34857     }
  34858 
  34859     // This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody`
  34860     // which calls `resolveInferredErrorSetPtr`.
  34861     const final_resolved_ty = func.resolvedErrorSetUnordered(ip);
  34862     assert(final_resolved_ty != .none);
  34863     return final_resolved_ty;
  34864 }
  34865 
  34866 pub fn resolveInferredErrorSetPtr(
  34867     sema: *Sema,
  34868     block: *Block,
  34869     src: LazySrcLoc,
  34870     ies: *InferredErrorSet,
  34871 ) CompileError!void {
  34872     const pt = sema.pt;
  34873     const ip = &pt.zcu.intern_pool;
  34874 
  34875     if (ies.resolved != .none) return;
  34876 
  34877     const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern());
  34878 
  34879     for (ies.inferred_error_sets.keys()) |other_ies_index| {
  34880         if (ies_index == other_ies_index) continue;
  34881         switch (try sema.resolveInferredErrorSet(block, src, other_ies_index)) {
  34882             .anyerror_type => {
  34883                 ies.resolved = .anyerror_type;
  34884                 return;
  34885             },
  34886             else => |error_set_ty_index| {
  34887                 const names = ip.indexToKey(error_set_ty_index).error_set_type.names;
  34888                 for (names.get(ip)) |name| {
  34889                     try ies.errors.put(sema.arena, name, {});
  34890                 }
  34891             },
  34892         }
  34893     }
  34894 
  34895     const resolved_error_set_ty = try pt.errorSetFromUnsortedNames(ies.errors.keys());
  34896     ies.resolved = resolved_error_set_ty.toIntern();
  34897 }
  34898 
  34899 fn resolveAdHocInferredErrorSet(
  34900     sema: *Sema,
  34901     block: *Block,
  34902     src: LazySrcLoc,
  34903     value: InternPool.Index,
  34904 ) CompileError!InternPool.Index {
  34905     const pt = sema.pt;
  34906     const zcu = pt.zcu;
  34907     const gpa = sema.gpa;
  34908     const ip = &zcu.intern_pool;
  34909     const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value));
  34910     if (new_ty == .none) return value;
  34911     return ip.getCoerced(gpa, pt.tid, value, new_ty);
  34912 }
  34913 
  34914 fn resolveAdHocInferredErrorSetTy(
  34915     sema: *Sema,
  34916     block: *Block,
  34917     src: LazySrcLoc,
  34918     ty: InternPool.Index,
  34919 ) CompileError!InternPool.Index {
  34920     const ies = sema.fn_ret_ty_ies orelse return .none;
  34921     const pt = sema.pt;
  34922     const zcu = pt.zcu;
  34923     const ip = &zcu.intern_pool;
  34924     const error_union_info = switch (ip.indexToKey(ty)) {
  34925         .error_union_type => |x| x,
  34926         else => return .none,
  34927     };
  34928     if (error_union_info.error_set_type != .adhoc_inferred_error_set_type)
  34929         return .none;
  34930 
  34931     try sema.resolveInferredErrorSetPtr(block, src, ies);
  34932     const new_ty = try pt.intern(.{ .error_union_type = .{
  34933         .error_set_type = ies.resolved,
  34934         .payload_type = error_union_info.payload_type,
  34935     } });
  34936     return new_ty;
  34937 }
  34938 
  34939 fn resolveInferredErrorSetTy(
  34940     sema: *Sema,
  34941     block: *Block,
  34942     src: LazySrcLoc,
  34943     ty: InternPool.Index,
  34944 ) CompileError!InternPool.Index {
  34945     const pt = sema.pt;
  34946     const zcu = pt.zcu;
  34947     const ip = &zcu.intern_pool;
  34948     if (ty == .anyerror_type) return ty;
  34949     switch (ip.indexToKey(ty)) {
  34950         .error_set_type => return ty,
  34951         .inferred_error_set_type => return sema.resolveInferredErrorSet(block, src, ty),
  34952         else => unreachable,
  34953     }
  34954 }
  34955 
  34956 fn structZirInfo(zir: Zir, zir_index: Zir.Inst.Index) struct {
  34957     /// fields_len
  34958     usize,
  34959     Zir.Inst.StructDecl.Small,
  34960     /// extra_index
  34961     usize,
  34962 } {
  34963     const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
  34964     assert(extended.opcode == .struct_decl);
  34965     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
  34966     var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
  34967 
  34968     const captures_len = if (small.has_captures_len) blk: {
  34969         const captures_len = zir.extra[extra_index];
  34970         extra_index += 1;
  34971         break :blk captures_len;
  34972     } else 0;
  34973 
  34974     const fields_len = if (small.has_fields_len) blk: {
  34975         const fields_len = zir.extra[extra_index];
  34976         extra_index += 1;
  34977         break :blk fields_len;
  34978     } else 0;
  34979 
  34980     const decls_len = if (small.has_decls_len) decls_len: {
  34981         const decls_len = zir.extra[extra_index];
  34982         extra_index += 1;
  34983         break :decls_len decls_len;
  34984     } else 0;
  34985 
  34986     extra_index += captures_len * 2;
  34987 
  34988     // The backing integer cannot be handled until `resolveStructLayout()`.
  34989     if (small.has_backing_int) {
  34990         const backing_int_body_len = zir.extra[extra_index];
  34991         extra_index += 1; // backing_int_body_len
  34992         if (backing_int_body_len == 0) {
  34993             extra_index += 1; // backing_int_ref
  34994         } else {
  34995             extra_index += backing_int_body_len; // backing_int_body_inst
  34996         }
  34997     }
  34998 
  34999     // Skip over decls.
  35000     extra_index += decls_len;
  35001 
  35002     return .{ fields_len, small, extra_index };
  35003 }
  35004 
  35005 fn structFields(
  35006     sema: *Sema,
  35007     struct_type: InternPool.LoadedStructType,
  35008 ) CompileError!void {
  35009     const pt = sema.pt;
  35010     const zcu = pt.zcu;
  35011     const gpa = zcu.gpa;
  35012     const ip = &zcu.intern_pool;
  35013     const namespace_index = struct_type.namespace;
  35014     const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
  35015     const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
  35016 
  35017     const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
  35018 
  35019     if (fields_len == 0) switch (struct_type.layout) {
  35020         .@"packed" => {
  35021             try sema.backingIntType(struct_type);
  35022             return;
  35023         },
  35024         .auto, .@"extern" => {
  35025             struct_type.setLayoutResolved(ip, 0, .none);
  35026             return;
  35027         },
  35028     };
  35029 
  35030     var block_scope: Block = .{
  35031         .parent = null,
  35032         .sema = sema,
  35033         .namespace = namespace_index,
  35034         .instructions = .{},
  35035         .inlining = null,
  35036         .comptime_reason = .{ .reason = .{
  35037             .src = .{
  35038                 .base_node_inst = struct_type.zir_index,
  35039                 .offset = .nodeOffset(.zero),
  35040             },
  35041             .r = .{ .simple = .struct_fields },
  35042         } },
  35043         .src_base_inst = struct_type.zir_index,
  35044         .type_name_ctx = struct_type.name,
  35045     };
  35046     defer assert(block_scope.instructions.items.len == 0);
  35047 
  35048     const Field = struct {
  35049         type_body_len: u32 = 0,
  35050         align_body_len: u32 = 0,
  35051         init_body_len: u32 = 0,
  35052         type_ref: Zir.Inst.Ref = .none,
  35053     };
  35054     const fields = try sema.arena.alloc(Field, fields_len);
  35055 
  35056     var any_inits = false;
  35057     var any_aligned = false;
  35058 
  35059     {
  35060         const bits_per_field = 4;
  35061         const fields_per_u32 = 32 / bits_per_field;
  35062         const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  35063         const flags_index = extra_index;
  35064         var bit_bag_index: usize = flags_index;
  35065         extra_index += bit_bags_count;
  35066         var cur_bit_bag: u32 = undefined;
  35067         var field_i: u32 = 0;
  35068         while (field_i < fields_len) : (field_i += 1) {
  35069             if (field_i % fields_per_u32 == 0) {
  35070                 cur_bit_bag = zir.extra[bit_bag_index];
  35071                 bit_bag_index += 1;
  35072             }
  35073             const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  35074             cur_bit_bag >>= 1;
  35075             const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
  35076             cur_bit_bag >>= 1;
  35077             const is_comptime = @as(u1, @truncate(cur_bit_bag)) != 0;
  35078             cur_bit_bag >>= 1;
  35079             const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
  35080             cur_bit_bag >>= 1;
  35081 
  35082             if (is_comptime) struct_type.setFieldComptime(ip, field_i);
  35083 
  35084             const field_name_zir: [:0]const u8 = zir.nullTerminatedString(@enumFromInt(zir.extra[extra_index]));
  35085             extra_index += 1; // field_name
  35086 
  35087             fields[field_i] = .{};
  35088 
  35089             if (has_type_body) {
  35090                 fields[field_i].type_body_len = zir.extra[extra_index];
  35091             } else {
  35092                 fields[field_i].type_ref = @enumFromInt(zir.extra[extra_index]);
  35093             }
  35094             extra_index += 1;
  35095 
  35096             // This string needs to outlive the ZIR code.
  35097             const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
  35098             assert(struct_type.addFieldName(ip, field_name) == null);
  35099 
  35100             if (has_align) {
  35101                 fields[field_i].align_body_len = zir.extra[extra_index];
  35102                 extra_index += 1;
  35103                 any_aligned = true;
  35104             }
  35105             if (has_init) {
  35106                 fields[field_i].init_body_len = zir.extra[extra_index];
  35107                 extra_index += 1;
  35108                 any_inits = true;
  35109             }
  35110         }
  35111     }
  35112 
  35113     // Next we do only types and alignments, saving the inits for a second pass,
  35114     // so that init values may depend on type layout.
  35115 
  35116     for (fields, 0..) |zir_field, field_i| {
  35117         const ty_src: LazySrcLoc = .{
  35118             .base_node_inst = struct_type.zir_index,
  35119             .offset = .{ .container_field_type = @intCast(field_i) },
  35120         };
  35121         const field_ty: Type = ty: {
  35122             if (zir_field.type_ref != .none) {
  35123                 break :ty try sema.resolveType(&block_scope, ty_src, zir_field.type_ref);
  35124             }
  35125             assert(zir_field.type_body_len != 0);
  35126             const body = zir.bodySlice(extra_index, zir_field.type_body_len);
  35127             extra_index += body.len;
  35128             const ty_ref = try sema.resolveInlineBody(&block_scope, body, zir_index);
  35129             break :ty try sema.analyzeAsType(&block_scope, ty_src, ty_ref);
  35130         };
  35131 
  35132         struct_type.field_types.get(ip)[field_i] = field_ty.toIntern();
  35133 
  35134         if (field_ty.zigTypeTag(zcu) == .@"opaque") {
  35135             const msg = msg: {
  35136                 const msg = try sema.errMsg(ty_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
  35137                 errdefer msg.destroy(sema.gpa);
  35138 
  35139                 try sema.addDeclaredHereNote(msg, field_ty);
  35140                 break :msg msg;
  35141             };
  35142             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35143         }
  35144         if (field_ty.zigTypeTag(zcu) == .noreturn) {
  35145             const msg = msg: {
  35146                 const msg = try sema.errMsg(ty_src, "struct fields cannot be 'noreturn'", .{});
  35147                 errdefer msg.destroy(sema.gpa);
  35148 
  35149                 try sema.addDeclaredHereNote(msg, field_ty);
  35150                 break :msg msg;
  35151             };
  35152             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35153         }
  35154         switch (struct_type.layout) {
  35155             .@"extern" => if (!try sema.validateExternType(field_ty, .struct_field)) {
  35156                 const msg = msg: {
  35157                     const msg = try sema.errMsg(ty_src, "extern structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  35158                     errdefer msg.destroy(sema.gpa);
  35159 
  35160                     try sema.explainWhyTypeIsNotExtern(msg, ty_src, field_ty, .struct_field);
  35161 
  35162                     try sema.addDeclaredHereNote(msg, field_ty);
  35163                     break :msg msg;
  35164                 };
  35165                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35166             },
  35167             .@"packed" => if (!try sema.validatePackedType(field_ty)) {
  35168                 const msg = msg: {
  35169                     const msg = try sema.errMsg(ty_src, "packed structs cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  35170                     errdefer msg.destroy(sema.gpa);
  35171 
  35172                     try sema.explainWhyTypeIsNotPacked(msg, ty_src, field_ty);
  35173 
  35174                     try sema.addDeclaredHereNote(msg, field_ty);
  35175                     break :msg msg;
  35176                 };
  35177                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35178             },
  35179             else => {},
  35180         }
  35181 
  35182         if (zir_field.align_body_len > 0) {
  35183             const body = zir.bodySlice(extra_index, zir_field.align_body_len);
  35184             extra_index += body.len;
  35185             const align_ref = try sema.resolveInlineBody(&block_scope, body, zir_index);
  35186             const align_src: LazySrcLoc = .{
  35187                 .base_node_inst = struct_type.zir_index,
  35188                 .offset = .{ .container_field_align = @intCast(field_i) },
  35189             };
  35190             const field_align = try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
  35191             struct_type.field_aligns.get(ip)[field_i] = field_align;
  35192         }
  35193 
  35194         extra_index += zir_field.init_body_len;
  35195     }
  35196 
  35197     struct_type.clearFieldTypesWip(ip);
  35198     if (!any_inits) struct_type.setHaveFieldInits(ip);
  35199 
  35200     try sema.flushExports();
  35201 }
  35202 
  35203 // This logic must be kept in sync with `structFields`
  35204 fn structFieldInits(
  35205     sema: *Sema,
  35206     struct_type: InternPool.LoadedStructType,
  35207 ) CompileError!void {
  35208     const pt = sema.pt;
  35209     const zcu = pt.zcu;
  35210     const ip = &zcu.intern_pool;
  35211 
  35212     assert(!struct_type.haveFieldInits(ip));
  35213 
  35214     const namespace_index = struct_type.namespace;
  35215     const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir.?;
  35216     const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
  35217     const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
  35218 
  35219     var block_scope: Block = .{
  35220         .parent = null,
  35221         .sema = sema,
  35222         .namespace = namespace_index,
  35223         .instructions = .{},
  35224         .inlining = null,
  35225         .comptime_reason = undefined, // set when `block_scope` is used
  35226         .src_base_inst = struct_type.zir_index,
  35227         .type_name_ctx = struct_type.name,
  35228     };
  35229     defer assert(block_scope.instructions.items.len == 0);
  35230 
  35231     const Field = struct {
  35232         type_body_len: u32 = 0,
  35233         align_body_len: u32 = 0,
  35234         init_body_len: u32 = 0,
  35235     };
  35236     const fields = try sema.arena.alloc(Field, fields_len);
  35237 
  35238     var any_inits = false;
  35239 
  35240     {
  35241         const bits_per_field = 4;
  35242         const fields_per_u32 = 32 / bits_per_field;
  35243         const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  35244         const flags_index = extra_index;
  35245         var bit_bag_index: usize = flags_index;
  35246         extra_index += bit_bags_count;
  35247         var cur_bit_bag: u32 = undefined;
  35248         var field_i: u32 = 0;
  35249         while (field_i < fields_len) : (field_i += 1) {
  35250             if (field_i % fields_per_u32 == 0) {
  35251                 cur_bit_bag = zir.extra[bit_bag_index];
  35252                 bit_bag_index += 1;
  35253             }
  35254             const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  35255             cur_bit_bag >>= 1;
  35256             const has_init = @as(u1, @truncate(cur_bit_bag)) != 0;
  35257             cur_bit_bag >>= 2;
  35258             const has_type_body = @as(u1, @truncate(cur_bit_bag)) != 0;
  35259             cur_bit_bag >>= 1;
  35260 
  35261             extra_index += 1; // field_name
  35262 
  35263             fields[field_i] = .{};
  35264 
  35265             if (has_type_body) fields[field_i].type_body_len = zir.extra[extra_index];
  35266             extra_index += 1;
  35267 
  35268             if (has_align) {
  35269                 fields[field_i].align_body_len = zir.extra[extra_index];
  35270                 extra_index += 1;
  35271             }
  35272             if (has_init) {
  35273                 fields[field_i].init_body_len = zir.extra[extra_index];
  35274                 extra_index += 1;
  35275                 any_inits = true;
  35276             }
  35277         }
  35278     }
  35279 
  35280     if (any_inits) {
  35281         for (fields, 0..) |zir_field, field_i| {
  35282             extra_index += zir_field.type_body_len;
  35283             extra_index += zir_field.align_body_len;
  35284             const body = zir.bodySlice(extra_index, zir_field.init_body_len);
  35285             extra_index += zir_field.init_body_len;
  35286 
  35287             if (body.len == 0) continue;
  35288 
  35289             // Pre-populate the type mapping the body expects to be there.
  35290             // In init bodies, the zir index of the struct itself is used
  35291             // to refer to the current field type.
  35292 
  35293             const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_i]);
  35294             const type_ref = Air.internedToRef(field_ty.toIntern());
  35295             try sema.inst_map.ensureSpaceForInstructions(sema.gpa, &.{zir_index});
  35296             sema.inst_map.putAssumeCapacity(zir_index, type_ref);
  35297 
  35298             const init_src: LazySrcLoc = .{
  35299                 .base_node_inst = struct_type.zir_index,
  35300                 .offset = .{ .container_field_value = @intCast(field_i) },
  35301             };
  35302 
  35303             block_scope.comptime_reason = .{ .reason = .{
  35304                 .src = init_src,
  35305                 .r = .{ .simple = .struct_field_default_value },
  35306             } };
  35307             const init = try sema.resolveInlineBody(&block_scope, body, zir_index);
  35308             const coerced = try sema.coerce(&block_scope, field_ty, init, init_src);
  35309             const default_val = try sema.resolveConstValue(&block_scope, init_src, coerced, null);
  35310 
  35311             if (default_val.canMutateComptimeVarState(zcu)) {
  35312                 const field_name = struct_type.fieldName(ip, field_i).unwrap().?;
  35313                 return sema.failWithContainsReferenceToComptimeVar(&block_scope, init_src, field_name, "field default value", default_val);
  35314             }
  35315             struct_type.field_inits.get(ip)[field_i] = default_val.toIntern();
  35316         }
  35317     }
  35318 
  35319     try sema.flushExports();
  35320 }
  35321 
  35322 fn unionFields(
  35323     sema: *Sema,
  35324     union_ty: InternPool.Index,
  35325     union_type: InternPool.LoadedUnionType,
  35326 ) CompileError!void {
  35327     const tracy = trace(@src());
  35328     defer tracy.end();
  35329 
  35330     const pt = sema.pt;
  35331     const zcu = pt.zcu;
  35332     const gpa = zcu.gpa;
  35333     const ip = &zcu.intern_pool;
  35334     const zir = zcu.namespacePtr(union_type.namespace).fileScope(zcu).zir.?;
  35335     const zir_index = union_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
  35336     const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
  35337     assert(extended.opcode == .union_decl);
  35338     const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
  35339     const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
  35340     var extra_index: usize = extra.end;
  35341 
  35342     const tag_type_ref: Zir.Inst.Ref = if (small.has_tag_type) blk: {
  35343         const ty_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  35344         extra_index += 1;
  35345         break :blk ty_ref;
  35346     } else .none;
  35347 
  35348     const captures_len = if (small.has_captures_len) blk: {
  35349         const captures_len = zir.extra[extra_index];
  35350         extra_index += 1;
  35351         break :blk captures_len;
  35352     } else 0;
  35353 
  35354     const body_len = if (small.has_body_len) blk: {
  35355         const body_len = zir.extra[extra_index];
  35356         extra_index += 1;
  35357         break :blk body_len;
  35358     } else 0;
  35359 
  35360     const fields_len = if (small.has_fields_len) blk: {
  35361         const fields_len = zir.extra[extra_index];
  35362         extra_index += 1;
  35363         break :blk fields_len;
  35364     } else 0;
  35365 
  35366     const decls_len = if (small.has_decls_len) decls_len: {
  35367         const decls_len = zir.extra[extra_index];
  35368         extra_index += 1;
  35369         break :decls_len decls_len;
  35370     } else 0;
  35371 
  35372     // Skip over captures and decls.
  35373     extra_index += captures_len * 2 + decls_len;
  35374 
  35375     const body = zir.bodySlice(extra_index, body_len);
  35376     extra_index += body.len;
  35377 
  35378     const src: LazySrcLoc = .{
  35379         .base_node_inst = union_type.zir_index,
  35380         .offset = .nodeOffset(.zero),
  35381     };
  35382 
  35383     var block_scope: Block = .{
  35384         .parent = null,
  35385         .sema = sema,
  35386         .namespace = union_type.namespace,
  35387         .instructions = .{},
  35388         .inlining = null,
  35389         .comptime_reason = .{ .reason = .{
  35390             .src = src,
  35391             .r = .{ .simple = .union_fields },
  35392         } },
  35393         .src_base_inst = union_type.zir_index,
  35394         .type_name_ctx = union_type.name,
  35395     };
  35396     defer assert(block_scope.instructions.items.len == 0);
  35397 
  35398     if (body.len != 0) {
  35399         _ = try sema.analyzeInlineBody(&block_scope, body, zir_index);
  35400     }
  35401 
  35402     var int_tag_ty: Type = undefined;
  35403     var enum_field_names: []InternPool.NullTerminatedString = &.{};
  35404     var enum_field_vals: std.AutoArrayHashMapUnmanaged(InternPool.Index, void) = .empty;
  35405     var explicit_tags_seen: []bool = &.{};
  35406     if (tag_type_ref != .none) {
  35407         const tag_ty_src: LazySrcLoc = .{
  35408             .base_node_inst = union_type.zir_index,
  35409             .offset = .{ .node_offset_container_tag = .zero },
  35410         };
  35411         const provided_ty = try sema.resolveType(&block_scope, tag_ty_src, tag_type_ref);
  35412         if (small.auto_enum_tag) {
  35413             // The provided type is an integer type and we must construct the enum tag type here.
  35414             int_tag_ty = provided_ty;
  35415             if (int_tag_ty.zigTypeTag(zcu) != .int and int_tag_ty.zigTypeTag(zcu) != .comptime_int) {
  35416                 return sema.fail(&block_scope, tag_ty_src, "expected integer tag type, found '{f}'", .{int_tag_ty.fmt(pt)});
  35417             }
  35418 
  35419             if (fields_len > 0) {
  35420                 const field_count_val = try pt.intValue(.comptime_int, fields_len - 1);
  35421                 if (!(try sema.intFitsInType(field_count_val, int_tag_ty, null))) {
  35422                     const msg = msg: {
  35423                         const msg = try sema.errMsg(tag_ty_src, "specified integer tag type cannot represent every field", .{});
  35424                         errdefer msg.destroy(sema.gpa);
  35425                         try sema.errNote(tag_ty_src, msg, "type '{f}' cannot fit values in range 0...{d}", .{
  35426                             int_tag_ty.fmt(pt),
  35427                             fields_len - 1,
  35428                         });
  35429                         break :msg msg;
  35430                     };
  35431                     return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35432                 }
  35433                 enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  35434                 try enum_field_vals.ensureTotalCapacity(sema.arena, fields_len);
  35435             }
  35436         } else {
  35437             // The provided type is the enum tag type.
  35438             const enum_type = switch (ip.indexToKey(provided_ty.toIntern())) {
  35439                 .enum_type => ip.loadEnumType(provided_ty.toIntern()),
  35440                 else => return sema.fail(&block_scope, tag_ty_src, "expected enum tag type, found '{f}'", .{provided_ty.fmt(pt)}),
  35441             };
  35442             union_type.setTagType(ip, provided_ty.toIntern());
  35443             // The fields of the union must match the enum exactly.
  35444             // A flag per field is used to check for missing and extraneous fields.
  35445             explicit_tags_seen = try sema.arena.alloc(bool, enum_type.names.len);
  35446             @memset(explicit_tags_seen, false);
  35447         }
  35448     } else {
  35449         // If auto_enum_tag is false, this is an untagged union. However, for semantic analysis
  35450         // purposes, we still auto-generate an enum tag type the same way. That the union is
  35451         // untagged is represented by the Type tag (union vs union_tagged).
  35452         enum_field_names = try sema.arena.alloc(InternPool.NullTerminatedString, fields_len);
  35453     }
  35454 
  35455     var field_types: std.ArrayListUnmanaged(InternPool.Index) = .empty;
  35456     var field_aligns: std.ArrayListUnmanaged(InternPool.Alignment) = .empty;
  35457 
  35458     try field_types.ensureTotalCapacityPrecise(sema.arena, fields_len);
  35459     if (small.any_aligned_fields)
  35460         try field_aligns.ensureTotalCapacityPrecise(sema.arena, fields_len);
  35461 
  35462     const bits_per_field = 4;
  35463     const fields_per_u32 = 32 / bits_per_field;
  35464     const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
  35465     var bit_bag_index: usize = extra_index;
  35466     extra_index += bit_bags_count;
  35467     var cur_bit_bag: u32 = undefined;
  35468     var field_i: u32 = 0;
  35469     var last_tag_val: ?Value = null;
  35470     while (field_i < fields_len) : (field_i += 1) {
  35471         if (field_i % fields_per_u32 == 0) {
  35472             cur_bit_bag = zir.extra[bit_bag_index];
  35473             bit_bag_index += 1;
  35474         }
  35475         const has_type = @as(u1, @truncate(cur_bit_bag)) != 0;
  35476         cur_bit_bag >>= 1;
  35477         const has_align = @as(u1, @truncate(cur_bit_bag)) != 0;
  35478         cur_bit_bag >>= 1;
  35479         const has_tag = @as(u1, @truncate(cur_bit_bag)) != 0;
  35480         cur_bit_bag >>= 1;
  35481         const unused = @as(u1, @truncate(cur_bit_bag)) != 0;
  35482         cur_bit_bag >>= 1;
  35483         _ = unused;
  35484 
  35485         const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]);
  35486         const field_name_zir = zir.nullTerminatedString(field_name_index);
  35487         extra_index += 1;
  35488 
  35489         const field_type_ref: Zir.Inst.Ref = if (has_type) blk: {
  35490             const field_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  35491             extra_index += 1;
  35492             break :blk field_type_ref;
  35493         } else .none;
  35494 
  35495         const align_ref: Zir.Inst.Ref = if (has_align) blk: {
  35496             const align_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  35497             extra_index += 1;
  35498             break :blk align_ref;
  35499         } else .none;
  35500 
  35501         const tag_ref: Air.Inst.Ref = if (has_tag) blk: {
  35502             const tag_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  35503             extra_index += 1;
  35504             break :blk try sema.resolveInst(tag_ref);
  35505         } else .none;
  35506 
  35507         const name_src: LazySrcLoc = .{
  35508             .base_node_inst = union_type.zir_index,
  35509             .offset = .{ .container_field_name = field_i },
  35510         };
  35511         const value_src: LazySrcLoc = .{
  35512             .base_node_inst = union_type.zir_index,
  35513             .offset = .{ .container_field_value = field_i },
  35514         };
  35515         const align_src: LazySrcLoc = .{
  35516             .base_node_inst = union_type.zir_index,
  35517             .offset = .{ .container_field_align = field_i },
  35518         };
  35519         const type_src: LazySrcLoc = .{
  35520             .base_node_inst = union_type.zir_index,
  35521             .offset = .{ .container_field_type = field_i },
  35522         };
  35523 
  35524         if (enum_field_vals.capacity() > 0) {
  35525             const enum_tag_val = if (tag_ref != .none) blk: {
  35526                 const coerced = try sema.coerce(&block_scope, int_tag_ty, tag_ref, value_src);
  35527                 const val = try sema.resolveConstDefinedValue(&block_scope, value_src, coerced, .{ .simple = .enum_field_tag_value });
  35528                 last_tag_val = val;
  35529 
  35530                 break :blk val;
  35531             } else blk: {
  35532                 if (last_tag_val) |last_tag| {
  35533                     const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag);
  35534                     if (result.overflow) return sema.fail(
  35535                         &block_scope,
  35536                         value_src,
  35537                         "enumeration value '{f}' too large for type '{f}'",
  35538                         .{ result.val.fmtValueSema(pt, sema), int_tag_ty.fmt(pt) },
  35539                     );
  35540                     last_tag_val = result.val;
  35541                 } else {
  35542                     last_tag_val = try pt.intValue(int_tag_ty, 0);
  35543                 }
  35544                 break :blk last_tag_val.?;
  35545             };
  35546             const gop = enum_field_vals.getOrPutAssumeCapacity(enum_tag_val.toIntern());
  35547             if (gop.found_existing) {
  35548                 const other_value_src: LazySrcLoc = .{
  35549                     .base_node_inst = union_type.zir_index,
  35550                     .offset = .{ .container_field_value = @intCast(gop.index) },
  35551                 };
  35552                 const msg = msg: {
  35553                     const msg = try sema.errMsg(
  35554                         value_src,
  35555                         "enum tag value {f} already taken",
  35556                         .{enum_tag_val.fmtValueSema(pt, sema)},
  35557                     );
  35558                     errdefer msg.destroy(gpa);
  35559                     try sema.errNote(other_value_src, msg, "other occurrence here", .{});
  35560                     break :msg msg;
  35561                 };
  35562                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35563             }
  35564         }
  35565 
  35566         // This string needs to outlive the ZIR code.
  35567         const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
  35568         if (enum_field_names.len != 0) {
  35569             enum_field_names[field_i] = field_name;
  35570         }
  35571 
  35572         const field_ty: Type = if (!has_type)
  35573             .void
  35574         else if (field_type_ref == .none)
  35575             .noreturn
  35576         else
  35577             try sema.resolveType(&block_scope, type_src, field_type_ref);
  35578 
  35579         if (explicit_tags_seen.len > 0) {
  35580             const tag_ty = union_type.tagTypeUnordered(ip);
  35581             const tag_info = ip.loadEnumType(tag_ty);
  35582             const enum_index = tag_info.nameIndex(ip, field_name) orelse {
  35583                 return sema.fail(&block_scope, name_src, "no field named '{f}' in enum '{f}'", .{
  35584                     field_name.fmt(ip), Type.fromInterned(tag_ty).fmt(pt),
  35585                 });
  35586             };
  35587 
  35588             // No check for duplicate because the check already happened in order
  35589             // to create the enum type in the first place.
  35590             assert(!explicit_tags_seen[enum_index]);
  35591             explicit_tags_seen[enum_index] = true;
  35592 
  35593             // Enforce the enum fields and the union fields being in the same order.
  35594             if (enum_index != field_i) {
  35595                 const msg = msg: {
  35596                     const enum_field_src: LazySrcLoc = .{
  35597                         .base_node_inst = Type.fromInterned(tag_ty).typeDeclInstAllowGeneratedTag(zcu).?,
  35598                         .offset = .{ .container_field_name = enum_index },
  35599                     };
  35600                     const msg = try sema.errMsg(name_src, "union field '{f}' ordered differently than corresponding enum field", .{
  35601                         field_name.fmt(ip),
  35602                     });
  35603                     errdefer msg.destroy(sema.gpa);
  35604                     try sema.errNote(enum_field_src, msg, "enum field here", .{});
  35605                     break :msg msg;
  35606                 };
  35607                 return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35608             }
  35609         }
  35610 
  35611         if (field_ty.zigTypeTag(zcu) == .@"opaque") {
  35612             const msg = msg: {
  35613                 const msg = try sema.errMsg(type_src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
  35614                 errdefer msg.destroy(sema.gpa);
  35615 
  35616                 try sema.addDeclaredHereNote(msg, field_ty);
  35617                 break :msg msg;
  35618             };
  35619             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35620         }
  35621         const layout = union_type.flagsUnordered(ip).layout;
  35622         if (layout == .@"extern" and
  35623             !try sema.validateExternType(field_ty, .union_field))
  35624         {
  35625             const msg = msg: {
  35626                 const msg = try sema.errMsg(type_src, "extern unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  35627                 errdefer msg.destroy(sema.gpa);
  35628 
  35629                 try sema.explainWhyTypeIsNotExtern(msg, type_src, field_ty, .union_field);
  35630 
  35631                 try sema.addDeclaredHereNote(msg, field_ty);
  35632                 break :msg msg;
  35633             };
  35634             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35635         } else if (layout == .@"packed" and !try sema.validatePackedType(field_ty)) {
  35636             const msg = msg: {
  35637                 const msg = try sema.errMsg(type_src, "packed unions cannot contain fields of type '{f}'", .{field_ty.fmt(pt)});
  35638                 errdefer msg.destroy(sema.gpa);
  35639 
  35640                 try sema.explainWhyTypeIsNotPacked(msg, type_src, field_ty);
  35641 
  35642                 try sema.addDeclaredHereNote(msg, field_ty);
  35643                 break :msg msg;
  35644             };
  35645             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35646         }
  35647 
  35648         field_types.appendAssumeCapacity(field_ty.toIntern());
  35649 
  35650         if (small.any_aligned_fields) {
  35651             field_aligns.appendAssumeCapacity(if (align_ref != .none)
  35652                 try sema.resolveAlign(&block_scope, align_src, align_ref)
  35653             else
  35654                 .none);
  35655         } else {
  35656             assert(align_ref == .none);
  35657         }
  35658     }
  35659 
  35660     union_type.setFieldTypes(ip, field_types.items);
  35661     union_type.setFieldAligns(ip, field_aligns.items);
  35662 
  35663     if (explicit_tags_seen.len > 0) {
  35664         const tag_ty = union_type.tagTypeUnordered(ip);
  35665         const tag_info = ip.loadEnumType(tag_ty);
  35666         if (tag_info.names.len > fields_len) {
  35667             const msg = msg: {
  35668                 const msg = try sema.errMsg(src, "enum field(s) missing in union", .{});
  35669                 errdefer msg.destroy(sema.gpa);
  35670 
  35671                 for (tag_info.names.get(ip), 0..) |field_name, field_index| {
  35672                     if (explicit_tags_seen[field_index]) continue;
  35673                     try sema.addFieldErrNote(.fromInterned(tag_ty), field_index, msg, "field '{f}' missing, declared here", .{
  35674                         field_name.fmt(ip),
  35675                     });
  35676                 }
  35677                 try sema.addDeclaredHereNote(msg, .fromInterned(tag_ty));
  35678                 break :msg msg;
  35679             };
  35680             return sema.failWithOwnedErrorMsg(&block_scope, msg);
  35681         }
  35682     } else if (enum_field_vals.count() > 0) {
  35683         const enum_ty = try sema.generateUnionTagTypeNumbered(&block_scope, enum_field_names, enum_field_vals.keys(), union_ty, union_type.name);
  35684         union_type.setTagType(ip, enum_ty);
  35685     } else {
  35686         const enum_ty = try sema.generateUnionTagTypeSimple(&block_scope, enum_field_names, union_ty, union_type.name);
  35687         union_type.setTagType(ip, enum_ty);
  35688     }
  35689 
  35690     try sema.flushExports();
  35691 }
  35692 
  35693 fn generateUnionTagTypeNumbered(
  35694     sema: *Sema,
  35695     block: *Block,
  35696     enum_field_names: []const InternPool.NullTerminatedString,
  35697     enum_field_vals: []const InternPool.Index,
  35698     union_type: InternPool.Index,
  35699     union_name: InternPool.NullTerminatedString,
  35700 ) !InternPool.Index {
  35701     const pt = sema.pt;
  35702     const zcu = pt.zcu;
  35703     const gpa = sema.gpa;
  35704     const ip = &zcu.intern_pool;
  35705 
  35706     const name = try ip.getOrPutStringFmt(
  35707         gpa,
  35708         pt.tid,
  35709         "@typeInfo({f}).@\"union\".tag_type.?",
  35710         .{union_name.fmt(ip)},
  35711         .no_embedded_nulls,
  35712     );
  35713 
  35714     const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{
  35715         .name = name,
  35716         .owner_union_ty = union_type,
  35717         .tag_ty = if (enum_field_vals.len == 0)
  35718             (try pt.intType(.unsigned, 0)).toIntern()
  35719         else
  35720             ip.typeOf(enum_field_vals[0]),
  35721         .names = enum_field_names,
  35722         .values = enum_field_vals,
  35723         .tag_mode = .explicit,
  35724         .parent_namespace = block.namespace,
  35725     });
  35726 
  35727     return enum_ty;
  35728 }
  35729 
  35730 fn generateUnionTagTypeSimple(
  35731     sema: *Sema,
  35732     block: *Block,
  35733     enum_field_names: []const InternPool.NullTerminatedString,
  35734     union_type: InternPool.Index,
  35735     union_name: InternPool.NullTerminatedString,
  35736 ) !InternPool.Index {
  35737     const pt = sema.pt;
  35738     const zcu = pt.zcu;
  35739     const ip = &zcu.intern_pool;
  35740     const gpa = sema.gpa;
  35741 
  35742     const name = try ip.getOrPutStringFmt(
  35743         gpa,
  35744         pt.tid,
  35745         "@typeInfo({f}).@\"union\".tag_type.?",
  35746         .{union_name.fmt(ip)},
  35747         .no_embedded_nulls,
  35748     );
  35749 
  35750     const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{
  35751         .name = name,
  35752         .owner_union_ty = union_type,
  35753         .tag_ty = (try pt.smallestUnsignedInt(enum_field_names.len -| 1)).toIntern(),
  35754         .names = enum_field_names,
  35755         .values = &.{},
  35756         .tag_mode = .auto,
  35757         .parent_namespace = block.namespace,
  35758     });
  35759 
  35760     return enum_ty;
  35761 }
  35762 
  35763 /// There is another implementation of this in `Type.onePossibleValue`. This one
  35764 /// in `Sema` is for calling during semantic analysis, and performs field resolution
  35765 /// to get the answer. The one in `Type` is for calling during codegen and asserts
  35766 /// that the types are already resolved.
  35767 /// TODO assert the return value matches `ty.onePossibleValue`
  35768 pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
  35769     const pt = sema.pt;
  35770     const zcu = pt.zcu;
  35771     const ip = &zcu.intern_pool;
  35772     return switch (ty.toIntern()) {
  35773         .u0_type,
  35774         .i0_type,
  35775         => try pt.intValue(ty, 0),
  35776         .u1_type,
  35777         .u8_type,
  35778         .i8_type,
  35779         .u16_type,
  35780         .i16_type,
  35781         .u29_type,
  35782         .u32_type,
  35783         .i32_type,
  35784         .u64_type,
  35785         .i64_type,
  35786         .u80_type,
  35787         .u128_type,
  35788         .i128_type,
  35789         .u256_type,
  35790         .usize_type,
  35791         .isize_type,
  35792         .c_char_type,
  35793         .c_short_type,
  35794         .c_ushort_type,
  35795         .c_int_type,
  35796         .c_uint_type,
  35797         .c_long_type,
  35798         .c_ulong_type,
  35799         .c_longlong_type,
  35800         .c_ulonglong_type,
  35801         .c_longdouble_type,
  35802         .f16_type,
  35803         .f32_type,
  35804         .f64_type,
  35805         .f80_type,
  35806         .f128_type,
  35807         .anyopaque_type,
  35808         .bool_type,
  35809         .type_type,
  35810         .anyerror_type,
  35811         .adhoc_inferred_error_set_type,
  35812         .comptime_int_type,
  35813         .comptime_float_type,
  35814         .enum_literal_type,
  35815         .ptr_usize_type,
  35816         .ptr_const_comptime_int_type,
  35817         .manyptr_u8_type,
  35818         .manyptr_const_u8_type,
  35819         .manyptr_const_u8_sentinel_0_type,
  35820         .slice_const_u8_type,
  35821         .slice_const_u8_sentinel_0_type,
  35822         .vector_8_i8_type,
  35823         .vector_16_i8_type,
  35824         .vector_32_i8_type,
  35825         .vector_64_i8_type,
  35826         .vector_1_u8_type,
  35827         .vector_2_u8_type,
  35828         .vector_4_u8_type,
  35829         .vector_8_u8_type,
  35830         .vector_16_u8_type,
  35831         .vector_32_u8_type,
  35832         .vector_64_u8_type,
  35833         .vector_2_i16_type,
  35834         .vector_4_i16_type,
  35835         .vector_8_i16_type,
  35836         .vector_16_i16_type,
  35837         .vector_32_i16_type,
  35838         .vector_4_u16_type,
  35839         .vector_8_u16_type,
  35840         .vector_16_u16_type,
  35841         .vector_32_u16_type,
  35842         .vector_2_i32_type,
  35843         .vector_4_i32_type,
  35844         .vector_8_i32_type,
  35845         .vector_16_i32_type,
  35846         .vector_4_u32_type,
  35847         .vector_8_u32_type,
  35848         .vector_16_u32_type,
  35849         .vector_2_i64_type,
  35850         .vector_4_i64_type,
  35851         .vector_8_i64_type,
  35852         .vector_2_u64_type,
  35853         .vector_4_u64_type,
  35854         .vector_8_u64_type,
  35855         .vector_1_u128_type,
  35856         .vector_2_u128_type,
  35857         .vector_1_u256_type,
  35858         .vector_4_f16_type,
  35859         .vector_8_f16_type,
  35860         .vector_16_f16_type,
  35861         .vector_32_f16_type,
  35862         .vector_2_f32_type,
  35863         .vector_4_f32_type,
  35864         .vector_8_f32_type,
  35865         .vector_16_f32_type,
  35866         .vector_2_f64_type,
  35867         .vector_4_f64_type,
  35868         .vector_8_f64_type,
  35869         .anyerror_void_error_union_type,
  35870         => null,
  35871         .void_type => Value.void,
  35872         .noreturn_type => Value.@"unreachable",
  35873         .anyframe_type => unreachable,
  35874         .null_type => Value.null,
  35875         .undefined_type => Value.undef,
  35876         .optional_noreturn_type => try pt.nullValue(ty),
  35877         .generic_poison_type => unreachable,
  35878         .empty_tuple_type => Value.empty_tuple,
  35879         // values, not types
  35880         .undef,
  35881         .undef_bool,
  35882         .undef_usize,
  35883         .undef_u1,
  35884         .zero,
  35885         .zero_usize,
  35886         .zero_u1,
  35887         .zero_u8,
  35888         .one,
  35889         .one_usize,
  35890         .one_u1,
  35891         .one_u8,
  35892         .four_u8,
  35893         .negative_one,
  35894         .void_value,
  35895         .unreachable_value,
  35896         .null_value,
  35897         .bool_true,
  35898         .bool_false,
  35899         .empty_tuple,
  35900         // invalid
  35901         .none,
  35902         => unreachable,
  35903 
  35904         _ => switch (ty.toIntern().unwrap(ip).getTag(ip)) {
  35905             .removed => unreachable,
  35906 
  35907             .type_int_signed, // i0 handled above
  35908             .type_int_unsigned, // u0 handled above
  35909             .type_pointer,
  35910             .type_slice,
  35911             .type_anyframe,
  35912             .type_error_union,
  35913             .type_anyerror_union,
  35914             .type_error_set,
  35915             .type_inferred_error_set,
  35916             .type_opaque,
  35917             .type_function,
  35918             => null,
  35919 
  35920             .simple_type, // handled above
  35921             // values, not types
  35922             .undef,
  35923             .simple_value,
  35924             .ptr_nav,
  35925             .ptr_uav,
  35926             .ptr_uav_aligned,
  35927             .ptr_comptime_alloc,
  35928             .ptr_comptime_field,
  35929             .ptr_int,
  35930             .ptr_eu_payload,
  35931             .ptr_opt_payload,
  35932             .ptr_elem,
  35933             .ptr_field,
  35934             .ptr_slice,
  35935             .opt_payload,
  35936             .opt_null,
  35937             .int_u8,
  35938             .int_u16,
  35939             .int_u32,
  35940             .int_i32,
  35941             .int_usize,
  35942             .int_comptime_int_u32,
  35943             .int_comptime_int_i32,
  35944             .int_small,
  35945             .int_positive,
  35946             .int_negative,
  35947             .int_lazy_align,
  35948             .int_lazy_size,
  35949             .error_set_error,
  35950             .error_union_error,
  35951             .error_union_payload,
  35952             .enum_literal,
  35953             .enum_tag,
  35954             .float_f16,
  35955             .float_f32,
  35956             .float_f64,
  35957             .float_f80,
  35958             .float_f128,
  35959             .float_c_longdouble_f80,
  35960             .float_c_longdouble_f128,
  35961             .float_comptime_float,
  35962             .variable,
  35963             .threadlocal_variable,
  35964             .@"extern",
  35965             .func_decl,
  35966             .func_instance,
  35967             .func_coerced,
  35968             .only_possible_value,
  35969             .union_value,
  35970             .bytes,
  35971             .aggregate,
  35972             .repeated,
  35973             // memoized value, not types
  35974             .memoized_call,
  35975             => unreachable,
  35976 
  35977             .type_array_big,
  35978             .type_array_small,
  35979             .type_vector,
  35980             .type_enum_auto,
  35981             .type_enum_explicit,
  35982             .type_enum_nonexhaustive,
  35983             .type_struct,
  35984             .type_struct_packed,
  35985             .type_struct_packed_inits,
  35986             .type_tuple,
  35987             .type_union,
  35988             => switch (ip.indexToKey(ty.toIntern())) {
  35989                 inline .array_type, .vector_type => |seq_type, seq_tag| {
  35990                     const has_sentinel = seq_tag == .array_type and seq_type.sentinel != .none;
  35991                     if (seq_type.len + @intFromBool(has_sentinel) == 0) return Value.fromInterned(try pt.intern(.{ .aggregate = .{
  35992                         .ty = ty.toIntern(),
  35993                         .storage = .{ .elems = &.{} },
  35994                     } }));
  35995 
  35996                     if (try sema.typeHasOnePossibleValue(.fromInterned(seq_type.child))) |opv| {
  35997                         return Value.fromInterned(try pt.intern(.{ .aggregate = .{
  35998                             .ty = ty.toIntern(),
  35999                             .storage = .{ .repeated_elem = opv.toIntern() },
  36000                         } }));
  36001                     }
  36002                     return null;
  36003                 },
  36004 
  36005                 .struct_type => {
  36006                     // Resolving the layout first helps to avoid loops.
  36007                     // If the type has a coherent layout, we can recurse through fields safely.
  36008                     try ty.resolveLayout(pt);
  36009 
  36010                     const struct_type = ip.loadStructType(ty.toIntern());
  36011 
  36012                     if (struct_type.field_types.len == 0) {
  36013                         // In this case the struct has no fields at all and
  36014                         // therefore has one possible value.
  36015                         return Value.fromInterned(try pt.intern(.{ .aggregate = .{
  36016                             .ty = ty.toIntern(),
  36017                             .storage = .{ .elems = &.{} },
  36018                         } }));
  36019                     }
  36020 
  36021                     const field_vals = try sema.arena.alloc(
  36022                         InternPool.Index,
  36023                         struct_type.field_types.len,
  36024                     );
  36025                     for (field_vals, 0..) |*field_val, i| {
  36026                         if (struct_type.fieldIsComptime(ip, i)) {
  36027                             try ty.resolveStructFieldInits(pt);
  36028                             field_val.* = struct_type.field_inits.get(ip)[i];
  36029                             continue;
  36030                         }
  36031                         const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
  36032                         if (try sema.typeHasOnePossibleValue(field_ty)) |field_opv| {
  36033                             field_val.* = field_opv.toIntern();
  36034                         } else return null;
  36035                     }
  36036 
  36037                     // In this case the struct has no runtime-known fields and
  36038                     // therefore has one possible value.
  36039                     return Value.fromInterned(try pt.intern(.{ .aggregate = .{
  36040                         .ty = ty.toIntern(),
  36041                         .storage = .{ .elems = field_vals },
  36042                     } }));
  36043                 },
  36044 
  36045                 .tuple_type => |tuple| {
  36046                     for (tuple.values.get(ip)) |val| {
  36047                         if (val == .none) return null;
  36048                     }
  36049                     // In this case the struct has all comptime-known fields and
  36050                     // therefore has one possible value.
  36051                     // TODO: write something like getCoercedInts to avoid needing to dupe
  36052                     return Value.fromInterned(try pt.intern(.{ .aggregate = .{
  36053                         .ty = ty.toIntern(),
  36054                         .storage = .{ .elems = try sema.arena.dupe(InternPool.Index, tuple.values.get(ip)) },
  36055                     } }));
  36056                 },
  36057 
  36058                 .union_type => {
  36059                     // Resolving the layout first helps to avoid loops.
  36060                     // If the type has a coherent layout, we can recurse through fields safely.
  36061                     try ty.resolveLayout(pt);
  36062 
  36063                     const union_obj = ip.loadUnionType(ty.toIntern());
  36064                     const tag_val = (try sema.typeHasOnePossibleValue(.fromInterned(union_obj.tagTypeUnordered(ip)))) orelse
  36065                         return null;
  36066                     if (union_obj.field_types.len == 0) {
  36067                         const only = try pt.intern(.{ .empty_enum_value = ty.toIntern() });
  36068                         return Value.fromInterned(only);
  36069                     }
  36070                     const only_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[0]);
  36071                     const val_val = (try sema.typeHasOnePossibleValue(only_field_ty)) orelse
  36072                         return null;
  36073                     const only = try pt.internUnion(.{
  36074                         .ty = ty.toIntern(),
  36075                         .tag = tag_val.toIntern(),
  36076                         .val = val_val.toIntern(),
  36077                     });
  36078                     return Value.fromInterned(only);
  36079                 },
  36080 
  36081                 .enum_type => {
  36082                     const enum_type = ip.loadEnumType(ty.toIntern());
  36083                     switch (enum_type.tag_mode) {
  36084                         .nonexhaustive => {
  36085                             if (enum_type.tag_ty == .comptime_int_type) return null;
  36086 
  36087                             if (try sema.typeHasOnePossibleValue(.fromInterned(enum_type.tag_ty))) |int_opv| {
  36088                                 const only = try pt.intern(.{ .enum_tag = .{
  36089                                     .ty = ty.toIntern(),
  36090                                     .int = int_opv.toIntern(),
  36091                                 } });
  36092                                 return Value.fromInterned(only);
  36093                             }
  36094 
  36095                             return null;
  36096                         },
  36097                         .auto, .explicit => {
  36098                             if (Type.fromInterned(enum_type.tag_ty).hasRuntimeBits(zcu)) return null;
  36099 
  36100                             return Value.fromInterned(switch (enum_type.names.len) {
  36101                                 0 => try pt.intern(.{ .empty_enum_value = ty.toIntern() }),
  36102                                 1 => try pt.intern(.{ .enum_tag = .{
  36103                                     .ty = ty.toIntern(),
  36104                                     .int = if (enum_type.values.len == 0)
  36105                                         (try pt.intValue(.fromInterned(enum_type.tag_ty), 0)).toIntern()
  36106                                     else
  36107                                         try ip.getCoercedInts(
  36108                                             zcu.gpa,
  36109                                             pt.tid,
  36110                                             ip.indexToKey(enum_type.values.get(ip)[0]).int,
  36111                                             enum_type.tag_ty,
  36112                                         ),
  36113                                 } }),
  36114                                 else => return null,
  36115                             });
  36116                         },
  36117                     }
  36118                 },
  36119 
  36120                 else => unreachable,
  36121             },
  36122 
  36123             .type_optional => {
  36124                 const payload_ip = ip.indexToKey(ty.toIntern()).opt_type;
  36125                 // Although ?noreturn is handled above, the element type
  36126                 // can be effectively noreturn for example via an empty
  36127                 // enum or error set.
  36128                 if (ip.isNoReturn(payload_ip)) return try pt.nullValue(ty);
  36129                 return null;
  36130             },
  36131         },
  36132     };
  36133 }
  36134 
  36135 /// Returns the type of the AIR instruction.
  36136 fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
  36137     return sema.getTmpAir().typeOf(inst, &sema.pt.zcu.intern_pool);
  36138 }
  36139 
  36140 pub fn getTmpAir(sema: Sema) Air {
  36141     return .{
  36142         .instructions = sema.air_instructions.slice(),
  36143         .extra = sema.air_extra,
  36144     };
  36145 }
  36146 
  36147 pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
  36148     const fields = std.meta.fields(@TypeOf(extra));
  36149     try sema.air_extra.ensureUnusedCapacity(sema.gpa, fields.len);
  36150     return sema.addExtraAssumeCapacity(extra);
  36151 }
  36152 
  36153 pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
  36154     const result: u32 = @intCast(sema.air_extra.items.len);
  36155     sema.air_extra.appendSliceAssumeCapacity(&payloadToExtraItems(extra));
  36156     return result;
  36157 }
  36158 
  36159 fn payloadToExtraItems(data: anytype) [@typeInfo(@TypeOf(data)).@"struct".fields.len]u32 {
  36160     const fields = @typeInfo(@TypeOf(data)).@"struct".fields;
  36161     var result: [fields.len]u32 = undefined;
  36162     inline for (&result, fields) |*val, field| {
  36163         val.* = switch (field.type) {
  36164             u32 => @field(data, field.name),
  36165             i32, Air.CondBr.BranchHints, Air.Asm.Flags => @bitCast(@field(data, field.name)),
  36166             Air.Inst.Ref, InternPool.Index => @intFromEnum(@field(data, field.name)),
  36167             else => @compileError("bad field type: " ++ @typeName(field.type)),
  36168         };
  36169     }
  36170     return result;
  36171 }
  36172 
  36173 fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void {
  36174     sema.air_extra.appendSliceAssumeCapacity(@ptrCast(refs));
  36175 }
  36176 
  36177 fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index {
  36178     const air_datas = sema.air_instructions.items(.data);
  36179     const air_tags = sema.air_instructions.items(.tag);
  36180     switch (air_tags[@intFromEnum(inst_index)]) {
  36181         .br => return air_datas[@intFromEnum(inst_index)].br.block_inst,
  36182         else => return null,
  36183     }
  36184 }
  36185 
  36186 fn isComptimeKnown(
  36187     sema: *Sema,
  36188     inst: Air.Inst.Ref,
  36189 ) !bool {
  36190     return (try sema.resolveValue(inst)) != null;
  36191 }
  36192 
  36193 fn analyzeComptimeAlloc(
  36194     sema: *Sema,
  36195     block: *Block,
  36196     src: LazySrcLoc,
  36197     var_type: Type,
  36198     alignment: Alignment,
  36199 ) CompileError!Air.Inst.Ref {
  36200     const pt = sema.pt;
  36201     const zcu = pt.zcu;
  36202 
  36203     // Needed to make an anon decl with type `var_type` (the `finish()` call below).
  36204     _ = try sema.typeHasOnePossibleValue(var_type);
  36205 
  36206     const ptr_type = try pt.ptrTypeSema(.{
  36207         .child = var_type.toIntern(),
  36208         .flags = .{
  36209             .alignment = alignment,
  36210             .address_space = target_util.defaultAddressSpace(zcu.getTarget(), .global_constant),
  36211         },
  36212     });
  36213 
  36214     const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment);
  36215 
  36216     return Air.internedToRef((try pt.intern(.{ .ptr = .{
  36217         .ty = ptr_type.toIntern(),
  36218         .base_addr = .{ .comptime_alloc = alloc },
  36219         .byte_offset = 0,
  36220     } })));
  36221 }
  36222 
  36223 fn resolveAddressSpace(
  36224     sema: *Sema,
  36225     block: *Block,
  36226     src: LazySrcLoc,
  36227     zir_ref: Zir.Inst.Ref,
  36228     ctx: std.builtin.AddressSpace.Context,
  36229 ) !std.builtin.AddressSpace {
  36230     const air_ref = try sema.resolveInst(zir_ref);
  36231     return sema.analyzeAsAddressSpace(block, src, air_ref, ctx);
  36232 }
  36233 
  36234 pub fn analyzeAsAddressSpace(
  36235     sema: *Sema,
  36236     block: *Block,
  36237     src: LazySrcLoc,
  36238     air_ref: Air.Inst.Ref,
  36239     ctx: std.builtin.AddressSpace.Context,
  36240 ) !std.builtin.AddressSpace {
  36241     const pt = sema.pt;
  36242     const addrspace_ty = try sema.getBuiltinType(src, .AddressSpace);
  36243     const coerced = try sema.coerce(block, addrspace_ty, air_ref, src);
  36244     const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{ .simple = .@"addrspace" });
  36245     const address_space = try sema.interpretBuiltinType(block, src, addrspace_val, std.builtin.AddressSpace);
  36246     const target = pt.zcu.getTarget();
  36247 
  36248     if (!target.cpu.supportsAddressSpace(address_space, ctx)) {
  36249         // TODO error messages could be made more elaborate here
  36250         const entity = switch (ctx) {
  36251             .function => "functions",
  36252             .variable => "mutable values",
  36253             .constant => "constant values",
  36254             .pointer => "pointers",
  36255         };
  36256         return sema.fail(
  36257             block,
  36258             src,
  36259             "{s} with address space '{s}' are not supported on {s}",
  36260             .{ entity, @tagName(address_space), @tagName(target.cpu.arch.family()) },
  36261         );
  36262     }
  36263 
  36264     return address_space;
  36265 }
  36266 
  36267 /// Asserts the value is a pointer and dereferences it.
  36268 /// Returns `null` if the pointer contents cannot be loaded at comptime.
  36269 fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
  36270     // TODO: audit use sites to eliminate this coercion
  36271     const pt = sema.pt;
  36272     const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty);
  36273     switch (try sema.pointerDerefExtra(block, src, coerced_ptr_val)) {
  36274         .runtime_load => return null,
  36275         .val => |v| return v,
  36276         .needed_well_defined => |ty| return sema.fail(
  36277             block,
  36278             src,
  36279             "comptime dereference requires '{f}' to have a well-defined layout",
  36280             .{ty.fmt(pt)},
  36281         ),
  36282         .out_of_bounds => |ty| return sema.fail(
  36283             block,
  36284             src,
  36285             "dereference of '{f}' exceeds bounds of containing decl of type '{f}'",
  36286             .{ ptr_ty.fmt(pt), ty.fmt(pt) },
  36287         ),
  36288     }
  36289 }
  36290 
  36291 const DerefResult = union(enum) {
  36292     runtime_load,
  36293     val: Value,
  36294     needed_well_defined: Type,
  36295     out_of_bounds: Type,
  36296 };
  36297 
  36298 fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value) CompileError!DerefResult {
  36299     const pt = sema.pt;
  36300     const ip = &pt.zcu.intern_pool;
  36301     switch (try sema.loadComptimePtr(block, src, ptr_val)) {
  36302         .success => |mv| return .{ .val = try mv.intern(pt, sema.arena) },
  36303         .runtime_load => return .runtime_load,
  36304         .undef => return sema.failWithUseOfUndef(block, src),
  36305         .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}),
  36306         .null_payload => return sema.fail(block, src, "attempt to use null value", .{}),
  36307         .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}),
  36308         .needed_well_defined => |ty| return .{ .needed_well_defined = ty },
  36309         .out_of_bounds => |ty| return .{ .out_of_bounds = ty },
  36310         .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}),
  36311     }
  36312 }
  36313 
  36314 /// Used to convert a u64 value to a usize value, emitting a compile error if the number
  36315 /// is too big to fit.
  36316 fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {
  36317     if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int;
  36318     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});
  36319 }
  36320 
  36321 /// For pointer-like optionals, it returns the pointer type. For pointers,
  36322 /// the type is returned unmodified.
  36323 /// This can return `error.AnalysisFail` because it sometimes requires resolving whether
  36324 /// a type has zero bits, which can cause a "foo depends on itself" compile error.
  36325 /// This logic must be kept in sync with `Type.isPtrLikeOptional`.
  36326 fn typePtrOrOptionalPtrTy(sema: *Sema, ty: Type) !?Type {
  36327     const pt = sema.pt;
  36328     const zcu = pt.zcu;
  36329     return switch (zcu.intern_pool.indexToKey(ty.toIntern())) {
  36330         .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  36331             .one, .many, .c => ty,
  36332             .slice => null,
  36333         },
  36334         .opt_type => |opt_child| switch (zcu.intern_pool.indexToKey(opt_child)) {
  36335             .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
  36336                 .slice, .c => null,
  36337                 .many, .one => {
  36338                     if (ptr_type.flags.is_allowzero) return null;
  36339 
  36340                     // optionals of zero sized types behave like bools, not pointers
  36341                     const payload_ty: Type = .fromInterned(opt_child);
  36342                     if ((try sema.typeHasOnePossibleValue(payload_ty)) != null) {
  36343                         return null;
  36344                     }
  36345 
  36346                     return payload_ty;
  36347                 },
  36348             },
  36349             else => null,
  36350         },
  36351         else => null,
  36352     };
  36353 }
  36354 
  36355 fn unionFieldIndex(
  36356     sema: *Sema,
  36357     block: *Block,
  36358     union_ty: Type,
  36359     field_name: InternPool.NullTerminatedString,
  36360     field_src: LazySrcLoc,
  36361 ) !u32 {
  36362     const pt = sema.pt;
  36363     const zcu = pt.zcu;
  36364     const ip = &zcu.intern_pool;
  36365     try union_ty.resolveFields(pt);
  36366     const union_obj = zcu.typeToUnion(union_ty).?;
  36367     const field_index = union_obj.loadTagType(ip).nameIndex(ip, field_name) orelse
  36368         return sema.failWithBadUnionFieldAccess(block, union_ty, union_obj, field_src, field_name);
  36369     return @intCast(field_index);
  36370 }
  36371 
  36372 fn structFieldIndex(
  36373     sema: *Sema,
  36374     block: *Block,
  36375     struct_ty: Type,
  36376     field_name: InternPool.NullTerminatedString,
  36377     field_src: LazySrcLoc,
  36378 ) !u32 {
  36379     const pt = sema.pt;
  36380     const zcu = pt.zcu;
  36381     const ip = &zcu.intern_pool;
  36382     try struct_ty.resolveFields(pt);
  36383     const struct_type = zcu.typeToStruct(struct_ty).?;
  36384     return struct_type.nameIndex(ip, field_name) orelse
  36385         return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name);
  36386 }
  36387 
  36388 const IntFromFloatMode = enum { exact, truncate };
  36389 
  36390 fn intFromFloat(
  36391     sema: *Sema,
  36392     block: *Block,
  36393     src: LazySrcLoc,
  36394     val: Value,
  36395     float_ty: Type,
  36396     int_ty: Type,
  36397     mode: IntFromFloatMode,
  36398 ) CompileError!Value {
  36399     const pt = sema.pt;
  36400     const zcu = pt.zcu;
  36401     if (float_ty.zigTypeTag(zcu) == .vector) {
  36402         const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(zcu));
  36403         for (result_data, 0..) |*scalar, i| {
  36404             const elem_val = try val.elemValue(pt, i);
  36405             scalar.* = (try sema.intFromFloatScalar(block, src, elem_val, int_ty.scalarType(zcu), mode)).toIntern();
  36406         }
  36407         return Value.fromInterned(try pt.intern(.{ .aggregate = .{
  36408             .ty = int_ty.toIntern(),
  36409             .storage = .{ .elems = result_data },
  36410         } }));
  36411     }
  36412     return sema.intFromFloatScalar(block, src, val, int_ty, mode);
  36413 }
  36414 
  36415 fn intFromFloatScalar(
  36416     sema: *Sema,
  36417     block: *Block,
  36418     src: LazySrcLoc,
  36419     val: Value,
  36420     int_ty: Type,
  36421     mode: IntFromFloatMode,
  36422 ) CompileError!Value {
  36423     const pt = sema.pt;
  36424     const zcu = pt.zcu;
  36425 
  36426     if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src);
  36427 
  36428     const float = val.toFloat(f128, zcu);
  36429     if (std.math.isNan(float)) {
  36430         return sema.fail(block, src, "float value NaN cannot be stored in integer type '{f}'", .{
  36431             int_ty.fmt(pt),
  36432         });
  36433     }
  36434     if (std.math.isInf(float)) {
  36435         return sema.fail(block, src, "float value Inf cannot be stored in integer type '{f}'", .{
  36436             int_ty.fmt(pt),
  36437         });
  36438     }
  36439 
  36440     var big_int: std.math.big.int.Mutable = .{
  36441         .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)),
  36442         .len = undefined,
  36443         .positive = undefined,
  36444     };
  36445     switch (big_int.setFloat(float, .trunc)) {
  36446         .inexact => switch (mode) {
  36447             .exact => return sema.fail(
  36448                 block,
  36449                 src,
  36450                 "fractional component prevents float value '{f}' from coercion to type '{f}'",
  36451                 .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) },
  36452             ),
  36453             .truncate => {},
  36454         },
  36455         .exact => {},
  36456     }
  36457     const cti_result = try pt.intValue_big(.comptime_int, big_int.toConst());
  36458     if (int_ty.toIntern() == .comptime_int_type) return cti_result;
  36459 
  36460     const int_info = int_ty.intInfo(zcu);
  36461     if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) {
  36462         return sema.fail(block, src, "float value '{f}' cannot be stored in integer type '{f}'", .{
  36463             val.fmtValueSema(pt, sema), int_ty.fmt(pt),
  36464         });
  36465     }
  36466     return pt.getCoerced(cti_result, int_ty);
  36467 }
  36468 
  36469 /// Asserts the value is an integer, and the destination type is ComptimeInt or Int.
  36470 /// Vectors are also accepted. Vector results are reduced with AND.
  36471 ///
  36472 /// If provided, `vector_index` reports the first element that failed the range check.
  36473 fn intFitsInType(
  36474     sema: *Sema,
  36475     val: Value,
  36476     ty: Type,
  36477     vector_index: ?*usize,
  36478 ) CompileError!bool {
  36479     const pt = sema.pt;
  36480     const zcu = pt.zcu;
  36481     if (ty.toIntern() == .comptime_int_type) return true;
  36482     const info = ty.intInfo(zcu);
  36483     switch (val.toIntern()) {
  36484         .zero_usize, .zero_u8 => return true,
  36485         else => switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  36486             .undef => return true,
  36487             .variable, .@"extern", .func, .ptr => {
  36488                 const target = zcu.getTarget();
  36489                 const ptr_bits = target.ptrBitWidth();
  36490                 return switch (info.signedness) {
  36491                     .signed => info.bits > ptr_bits,
  36492                     .unsigned => info.bits >= ptr_bits,
  36493                 };
  36494             },
  36495             .int => |int| switch (int.storage) {
  36496                 .u64, .i64, .big_int => {
  36497                     var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
  36498                     const big_int = int.storage.toBigInt(&buffer);
  36499                     return big_int.fitsInTwosComp(info.signedness, info.bits);
  36500                 },
  36501                 .lazy_align => |lazy_ty| {
  36502                     const max_needed_bits = @as(u16, 16) + @intFromBool(info.signedness == .signed);
  36503                     // If it is u16 or bigger we know the alignment fits without resolving it.
  36504                     if (info.bits >= max_needed_bits) return true;
  36505                     const x = try Type.fromInterned(lazy_ty).abiAlignmentSema(pt);
  36506                     if (x == .none) return true;
  36507                     const actual_needed_bits = @as(usize, x.toLog2Units()) + 1 + @intFromBool(info.signedness == .signed);
  36508                     return info.bits >= actual_needed_bits;
  36509                 },
  36510                 .lazy_size => |lazy_ty| {
  36511                     const max_needed_bits = @as(u16, 64) + @intFromBool(info.signedness == .signed);
  36512                     // If it is u64 or bigger we know the size fits without resolving it.
  36513                     if (info.bits >= max_needed_bits) return true;
  36514                     const x = try Type.fromInterned(lazy_ty).abiSizeSema(pt);
  36515                     if (x == 0) return true;
  36516                     const actual_needed_bits = std.math.log2(x) + 1 + @intFromBool(info.signedness == .signed);
  36517                     return info.bits >= actual_needed_bits;
  36518                 },
  36519             },
  36520             .aggregate => |aggregate| {
  36521                 assert(ty.zigTypeTag(zcu) == .vector);
  36522                 return switch (aggregate.storage) {
  36523                     .bytes => |bytes| for (bytes.toSlice(ty.vectorLen(zcu), &zcu.intern_pool), 0..) |byte, i| {
  36524                         if (byte == 0) continue;
  36525                         const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed);
  36526                         if (info.bits >= actual_needed_bits) continue;
  36527                         if (vector_index) |vi| vi.* = i;
  36528                         break false;
  36529                     } else true,
  36530                     .elems, .repeated_elem => for (switch (aggregate.storage) {
  36531                         .bytes => unreachable,
  36532                         .elems => |elems| elems,
  36533                         .repeated_elem => |elem| @as(*const [1]InternPool.Index, &elem),
  36534                     }, 0..) |elem, i| {
  36535                         if (try sema.intFitsInType(Value.fromInterned(elem), ty.scalarType(zcu), null)) continue;
  36536                         if (vector_index) |vi| vi.* = i;
  36537                         break false;
  36538                     } else true,
  36539                 };
  36540             },
  36541             else => unreachable,
  36542         },
  36543     }
  36544 }
  36545 
  36546 fn intInRange(sema: *Sema, tag_ty: Type, int_val: Value, end: usize) !bool {
  36547     const pt = sema.pt;
  36548     if (!(try int_val.compareAllWithZeroSema(.gte, pt))) return false;
  36549     const end_val = try pt.intValue(tag_ty, end);
  36550     if (!(try sema.compareAll(int_val, .lt, end_val, tag_ty))) return false;
  36551     return true;
  36552 }
  36553 
  36554 /// Asserts the type is an enum.
  36555 fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
  36556     const pt = sema.pt;
  36557     const zcu = pt.zcu;
  36558     const enum_type = zcu.intern_pool.loadEnumType(ty.toIntern());
  36559     assert(enum_type.tag_mode != .nonexhaustive);
  36560     // The `tagValueIndex` function call below relies on the type being the integer tag type.
  36561     // `getCoerced` assumes the value will fit the new type.
  36562     if (!(try sema.intFitsInType(int, .fromInterned(enum_type.tag_ty), null))) return false;
  36563     const int_coerced = try pt.getCoerced(int, .fromInterned(enum_type.tag_ty));
  36564 
  36565     return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null;
  36566 }
  36567 
  36568 /// Asserts the values are comparable. Both operands have type `ty`.
  36569 /// For vectors, returns true if the comparison is true for ALL elements.
  36570 ///
  36571 /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)`
  36572 fn compareAll(
  36573     sema: *Sema,
  36574     lhs: Value,
  36575     op: std.math.CompareOperator,
  36576     rhs: Value,
  36577     ty: Type,
  36578 ) CompileError!bool {
  36579     const pt = sema.pt;
  36580     const zcu = pt.zcu;
  36581     if (ty.zigTypeTag(zcu) == .vector) {
  36582         var i: usize = 0;
  36583         while (i < ty.vectorLen(zcu)) : (i += 1) {
  36584             const lhs_elem = try lhs.elemValue(pt, i);
  36585             const rhs_elem = try rhs.elemValue(pt, i);
  36586             if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu)))) {
  36587                 return false;
  36588             }
  36589         }
  36590         return true;
  36591     }
  36592     return sema.compareScalar(lhs, op, rhs, ty);
  36593 }
  36594 
  36595 /// Asserts the values are comparable. Both operands have type `ty`.
  36596 fn compareScalar(
  36597     sema: *Sema,
  36598     lhs: Value,
  36599     op: std.math.CompareOperator,
  36600     rhs: Value,
  36601     ty: Type,
  36602 ) CompileError!bool {
  36603     const pt = sema.pt;
  36604     const coerced_lhs = try pt.getCoerced(lhs, ty);
  36605     const coerced_rhs = try pt.getCoerced(rhs, ty);
  36606 
  36607     // Equality comparisons of signed zero and NaN need to use floating point semantics
  36608     if (coerced_lhs.isFloat(pt.zcu) or coerced_rhs.isFloat(pt.zcu))
  36609         return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt);
  36610 
  36611     switch (op) {
  36612         .eq => return sema.valuesEqual(coerced_lhs, coerced_rhs, ty),
  36613         .neq => return !(try sema.valuesEqual(coerced_lhs, coerced_rhs, ty)),
  36614         else => return Value.compareHeteroSema(coerced_lhs, op, coerced_rhs, pt),
  36615     }
  36616 }
  36617 
  36618 fn valuesEqual(
  36619     sema: *Sema,
  36620     lhs: Value,
  36621     rhs: Value,
  36622     ty: Type,
  36623 ) CompileError!bool {
  36624     return lhs.eql(rhs, ty, sema.pt.zcu);
  36625 }
  36626 
  36627 /// Asserts the values are comparable vectors of type `ty`.
  36628 fn compareVector(
  36629     sema: *Sema,
  36630     lhs: Value,
  36631     op: std.math.CompareOperator,
  36632     rhs: Value,
  36633     ty: Type,
  36634 ) !Value {
  36635     const pt = sema.pt;
  36636     const zcu = pt.zcu;
  36637     assert(ty.zigTypeTag(zcu) == .vector);
  36638     const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(zcu));
  36639     for (result_data, 0..) |*scalar, i| {
  36640         const lhs_elem = try lhs.elemValue(pt, i);
  36641         const rhs_elem = try rhs.elemValue(pt, i);
  36642         if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) {
  36643             scalar.* = .undef_bool;
  36644         } else {
  36645             const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu));
  36646             scalar.* = Value.makeBool(res_bool).toIntern();
  36647         }
  36648     }
  36649     return Value.fromInterned(try pt.intern(.{ .aggregate = .{
  36650         .ty = (try pt.vectorType(.{ .len = ty.vectorLen(zcu), .child = .bool_type })).toIntern(),
  36651         .storage = .{ .elems = result_data },
  36652     } }));
  36653 }
  36654 
  36655 /// Merge lhs with rhs.
  36656 /// Asserts that lhs and rhs are both error sets and are resolved.
  36657 fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {
  36658     const pt = sema.pt;
  36659     const ip = &pt.zcu.intern_pool;
  36660     const arena = sema.arena;
  36661     const lhs_names = lhs.errorSetNames(pt.zcu);
  36662     const rhs_names = rhs.errorSetNames(pt.zcu);
  36663     var names: InferredErrorSet.NameMap = .{};
  36664     try names.ensureUnusedCapacity(arena, lhs_names.len);
  36665 
  36666     for (0..lhs_names.len) |lhs_index| {
  36667         names.putAssumeCapacityNoClobber(lhs_names.get(ip)[lhs_index], {});
  36668     }
  36669     for (0..rhs_names.len) |rhs_index| {
  36670         try names.put(arena, rhs_names.get(ip)[rhs_index], {});
  36671     }
  36672 
  36673     return pt.errorSetFromUnsortedNames(names.keys());
  36674 }
  36675 
  36676 /// Avoids crashing the compiler when asking if inferred allocations are noreturn.
  36677 fn isNoReturn(sema: *Sema, ref: Air.Inst.Ref) bool {
  36678     if (ref == .unreachable_value) return true;
  36679     if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) {
  36680         .inferred_alloc, .inferred_alloc_comptime => return false,
  36681         else => {},
  36682     };
  36683     return sema.typeOf(ref).isNoReturn(sema.pt.zcu);
  36684 }
  36685 
  36686 /// Avoids crashing the compiler when asking if inferred allocations are known to be a certain zig type.
  36687 fn isKnownZigType(sema: *Sema, ref: Air.Inst.Ref, tag: std.builtin.TypeId) bool {
  36688     if (ref.toIndex()) |inst| switch (sema.air_instructions.items(.tag)[@intFromEnum(inst)]) {
  36689         .inferred_alloc, .inferred_alloc_comptime => return false,
  36690         else => {},
  36691     };
  36692     return sema.typeOf(ref).zigTypeTag(sema.pt.zcu) == tag;
  36693 }
  36694 
  36695 pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
  36696     const pt = sema.pt;
  36697     if (!pt.zcu.comp.incremental) return;
  36698 
  36699     const gop = try sema.dependencies.getOrPut(sema.gpa, dependee);
  36700     if (gop.found_existing) return;
  36701 
  36702     // Avoid creating dependencies on ourselves. This situation can arise when we analyze the fields
  36703     // of a type and they use `@This()`. This dependency would be unnecessary, and in fact would
  36704     // just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve
  36705     // the loop.
  36706     // Note that this also disallows a `nav_val`
  36707     switch (sema.owner.unwrap()) {
  36708         .nav_val => |this_nav| switch (dependee) {
  36709             .nav_val => |other_nav| if (this_nav == other_nav) return,
  36710             else => {},
  36711         },
  36712         .nav_ty => |this_nav| switch (dependee) {
  36713             .nav_ty => |other_nav| if (this_nav == other_nav) return,
  36714             else => {},
  36715         },
  36716         else => {},
  36717     }
  36718 
  36719     try pt.addDependency(sema.owner, dependee);
  36720 }
  36721 
  36722 fn isComptimeMutablePtr(sema: *Sema, val: Value) bool {
  36723     return switch (sema.pt.zcu.intern_pool.indexToKey(val.toIntern())) {
  36724         .slice => |slice| sema.isComptimeMutablePtr(Value.fromInterned(slice.ptr)),
  36725         .ptr => |ptr| switch (ptr.base_addr) {
  36726             .uav, .nav, .int => false,
  36727             .comptime_field => true,
  36728             .comptime_alloc => |alloc_index| !sema.getComptimeAlloc(alloc_index).is_const,
  36729             .eu_payload, .opt_payload => |base| sema.isComptimeMutablePtr(Value.fromInterned(base)),
  36730             .arr_elem, .field => |bi| sema.isComptimeMutablePtr(Value.fromInterned(bi.base)),
  36731         },
  36732         else => false,
  36733     };
  36734 }
  36735 
  36736 fn checkRuntimeValue(sema: *Sema, ptr: Air.Inst.Ref) bool {
  36737     const val = ptr.toInterned() orelse return true;
  36738     return !Value.fromInterned(val).canMutateComptimeVarState(sema.pt.zcu);
  36739 }
  36740 
  36741 fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Air.Inst.Ref) CompileError!void {
  36742     if (sema.checkRuntimeValue(val)) return;
  36743     return sema.failWithOwnedErrorMsg(block, msg: {
  36744         const msg = try sema.errMsg(val_src, "runtime value contains reference to comptime var", .{});
  36745         errdefer msg.destroy(sema.gpa);
  36746         try sema.errNote(val_src, msg, "comptime var pointers are not available at runtime", .{});
  36747         const pt = sema.pt;
  36748         const zcu = pt.zcu;
  36749         const val_str = try zcu.intern_pool.getOrPutString(zcu.gpa, pt.tid, "runtime_value", .no_embedded_nulls);
  36750         try sema.explainWhyValueContainsReferenceToComptimeVar(msg, val_src, val_str, .fromInterned(val.toInterned().?));
  36751         break :msg msg;
  36752     });
  36753 }
  36754 
  36755 fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError {
  36756     return sema.failWithOwnedErrorMsg(block, msg: {
  36757         const msg = try sema.errMsg(src, "{s} contains reference to comptime var", .{kind_of_value});
  36758         errdefer msg.destroy(sema.gpa);
  36759         if (val) |v| try sema.explainWhyValueContainsReferenceToComptimeVar(msg, src, value_name, v);
  36760         break :msg msg;
  36761     });
  36762 }
  36763 
  36764 fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, val: Value) Allocator.Error!void {
  36765     // Our goal is something like this:
  36766     //   note: '(value.? catch unreachable)[0]' points to 'v0.?.foo'
  36767     //   note: '(v0.?.bar catch unreachable)' points to 'v1'
  36768     //   note: 'v1.?' points to a comptime var
  36769 
  36770     var intermediate_value_count: u32 = 0;
  36771     var cur_val: Value = val;
  36772     while (true) {
  36773         switch (try sema.notePathToComptimeAllocPtr(msg, src, cur_val, intermediate_value_count, value_name)) {
  36774             .done => return,
  36775             .new_val => |new_val| {
  36776                 intermediate_value_count += 1;
  36777                 cur_val = new_val;
  36778             },
  36779         }
  36780     }
  36781 }
  36782 
  36783 fn notePathToComptimeAllocPtr(
  36784     sema: *Sema,
  36785     msg: *Zcu.ErrorMsg,
  36786     src: LazySrcLoc,
  36787     val: Value,
  36788     intermediate_value_count: u32,
  36789     start_value_name: InternPool.NullTerminatedString,
  36790 ) Allocator.Error!union(enum) {
  36791     done,
  36792     new_val: Value,
  36793 } {
  36794     const arena = sema.arena;
  36795     const pt = sema.pt;
  36796     const zcu = pt.zcu;
  36797     const ip = &zcu.intern_pool;
  36798 
  36799     var first_path: std.ArrayListUnmanaged(u8) = .empty;
  36800     if (intermediate_value_count == 0) {
  36801         try first_path.print(arena, "{f}", .{start_value_name.fmt(ip)});
  36802     } else {
  36803         try first_path.print(arena, "v{d}", .{intermediate_value_count - 1});
  36804     }
  36805 
  36806     const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path);
  36807 
  36808     switch (ip.indexToKey(comptime_ptr.toIntern()).ptr.base_addr) {
  36809         .comptime_field => {
  36810             try sema.errNote(src, msg, "'{s}' points to comptime field", .{first_path.items});
  36811             return .done;
  36812         },
  36813         .comptime_alloc => |idx| {
  36814             const cta = sema.getComptimeAlloc(idx);
  36815             if (!cta.is_const) {
  36816                 try sema.errNote(cta.src, msg, "'{s}' points to comptime var declared here", .{first_path.items});
  36817                 return .done;
  36818             }
  36819         },
  36820         else => {}, // there will be another stage
  36821     }
  36822 
  36823     const derivation = comptime_ptr.pointerDerivationAdvanced(arena, pt, false, sema) catch |err| switch (err) {
  36824         error.OutOfMemory => |e| return e,
  36825         error.AnalysisFail => unreachable,
  36826     };
  36827 
  36828     var second_path_aw: std.io.Writer.Allocating = .init(arena);
  36829     defer second_path_aw.deinit();
  36830     const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count});
  36831     const deriv_start = @import("print_value.zig").printPtrDerivation(
  36832         derivation,
  36833         &second_path_aw.writer,
  36834         pt,
  36835         .lvalue,
  36836         .{ .str = inter_name },
  36837         20,
  36838     ) catch return error.OutOfMemory;
  36839 
  36840     switch (deriv_start) {
  36841         .int, .nav_ptr => unreachable,
  36842         .uav_ptr => |uav| {
  36843             try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.getWritten() });
  36844             return .{ .new_val = .fromInterned(uav.val) };
  36845         },
  36846         .comptime_alloc_ptr => |cta_info| {
  36847             try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.getWritten() });
  36848             const cta = sema.getComptimeAlloc(cta_info.idx);
  36849             if (cta.is_const) {
  36850                 return .{ .new_val = cta_info.val };
  36851             } else {
  36852                 try sema.errNote(cta.src, msg, "'{s}' is a comptime var declared here", .{inter_name});
  36853                 return .done;
  36854             }
  36855         },
  36856         .comptime_field_ptr => {
  36857             try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.getWritten() });
  36858             try sema.errNote(src, msg, "'{s}' is a comptime field", .{inter_name});
  36859             return .done;
  36860         },
  36861         .eu_payload_ptr,
  36862         .opt_payload_ptr,
  36863         .field_ptr,
  36864         .elem_ptr,
  36865         .offset_and_cast,
  36866         => unreachable,
  36867     }
  36868 }
  36869 
  36870 fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayListUnmanaged(u8)) Allocator.Error!Value {
  36871     const pt = sema.pt;
  36872     const zcu = pt.zcu;
  36873     const ip = &zcu.intern_pool;
  36874     const arena = sema.arena;
  36875     assert(val.canMutateComptimeVarState(zcu));
  36876     switch (ip.indexToKey(val.toIntern())) {
  36877         .ptr => return val,
  36878         .error_union => |eu| {
  36879             try path.insert(arena, 0, '(');
  36880             try path.appendSlice(arena, " catch unreachable)");
  36881             return sema.notePathToComptimeAllocPtrInner(.fromInterned(eu.val.payload), path);
  36882         },
  36883         .slice => |slice| {
  36884             try path.appendSlice(arena, ".ptr");
  36885             return sema.notePathToComptimeAllocPtrInner(.fromInterned(slice.ptr), path);
  36886         },
  36887         .opt => |opt| {
  36888             try path.appendSlice(arena, ".?");
  36889             return sema.notePathToComptimeAllocPtrInner(.fromInterned(opt.val), path);
  36890         },
  36891         .un => |un| {
  36892             assert(un.tag != .none);
  36893             const union_ty: Type = .fromInterned(un.ty);
  36894             const backing_enum = union_ty.unionTagTypeHypothetical(zcu);
  36895             const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?;
  36896             const field_name = backing_enum.enumFieldName(field_idx, zcu);
  36897             try path.print(arena, ".{f}", .{field_name.fmt(ip)});
  36898             return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path);
  36899         },
  36900         .aggregate => |agg| {
  36901             const elem: InternPool.Index, const elem_idx: usize = switch (agg.storage) {
  36902                 .bytes => unreachable,
  36903                 .repeated_elem => |elem| .{ elem, 0 },
  36904                 .elems => |elems| for (elems, 0..) |elem, elem_idx| {
  36905                     if (Value.fromInterned(elem).canMutateComptimeVarState(zcu)) {
  36906                         break .{ elem, elem_idx };
  36907                     }
  36908                 } else unreachable,
  36909             };
  36910             const agg_ty: Type = .fromInterned(agg.ty);
  36911             switch (agg_ty.zigTypeTag(zcu)) {
  36912                 .array, .vector => try path.print(arena, "[{d}]", .{elem_idx}),
  36913                 .pointer => switch (elem_idx) {
  36914                     Value.slice_ptr_index => try path.appendSlice(arena, ".ptr"),
  36915                     Value.slice_len_index => try path.appendSlice(arena, ".len"),
  36916                     else => unreachable,
  36917                 },
  36918                 .@"struct" => if (agg_ty.isTuple(zcu)) {
  36919                     try path.print(arena, "[{d}]", .{elem_idx});
  36920                 } else {
  36921                     const name = agg_ty.structFieldName(elem_idx, zcu).unwrap().?;
  36922                     try path.print(arena, ".{f}", .{name.fmt(ip)});
  36923                 },
  36924                 else => unreachable,
  36925             }
  36926             return sema.notePathToComptimeAllocPtrInner(.fromInterned(elem), path);
  36927         },
  36928         else => unreachable,
  36929     }
  36930 }
  36931 
  36932 /// Returns true if any value contained in `val` is undefined.
  36933 fn anyUndef(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) !bool {
  36934     const pt = sema.pt;
  36935     const zcu = pt.zcu;
  36936     return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
  36937         .undef => true,
  36938         .simple_value => |v| v == .undefined,
  36939         .slice => {
  36940             // If the slice contents are runtime-known, reification will fail later on with a
  36941             // specific error message.
  36942             const arr = try sema.maybeDerefSliceAsArray(block, src, val) orelse return false;
  36943             return sema.anyUndef(block, src, arr);
  36944         },
  36945         .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| {
  36946             const elem = zcu.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i];
  36947             if (try sema.anyUndef(block, src, Value.fromInterned(elem))) break true;
  36948         } else false,
  36949         else => false,
  36950     };
  36951 }
  36952 
  36953 /// Asserts that `slice_val` is a slice of `u8`.
  36954 fn sliceToIpString(
  36955     sema: *Sema,
  36956     block: *Block,
  36957     src: LazySrcLoc,
  36958     slice_val: Value,
  36959     reason: ComptimeReason,
  36960 ) CompileError!InternPool.NullTerminatedString {
  36961     const pt = sema.pt;
  36962     const zcu = pt.zcu;
  36963     const slice_ty = slice_val.typeOf(zcu);
  36964     assert(slice_ty.isSlice(zcu));
  36965     assert(slice_ty.childType(zcu).toIntern() == .u8_type);
  36966     const array_val = try sema.derefSliceAsArray(block, src, slice_val, reason);
  36967     const array_ty = array_val.typeOf(zcu);
  36968     return array_val.toIpString(array_ty, pt);
  36969 }
  36970 
  36971 /// Given a slice value, attempts to dereference it into a comptime-known array.
  36972 /// Emits a compile error if the contents of the slice are not comptime-known.
  36973 /// Asserts that `slice_val` is a slice.
  36974 fn derefSliceAsArray(
  36975     sema: *Sema,
  36976     block: *Block,
  36977     src: LazySrcLoc,
  36978     slice_val: Value,
  36979     reason: ComptimeReason,
  36980 ) CompileError!Value {
  36981     return try sema.maybeDerefSliceAsArray(block, src, slice_val) orelse {
  36982         return sema.failWithNeededComptime(block, src, reason);
  36983     };
  36984 }
  36985 
  36986 /// Given a slice value, attempts to dereference it into a comptime-known array.
  36987 /// Returns `null` if the contents of the slice are not comptime-known.
  36988 /// Asserts that `slice_val` is a slice.
  36989 fn maybeDerefSliceAsArray(
  36990     sema: *Sema,
  36991     block: *Block,
  36992     src: LazySrcLoc,
  36993     slice_val: Value,
  36994 ) CompileError!?Value {
  36995     const pt = sema.pt;
  36996     const zcu = pt.zcu;
  36997     const ip = &zcu.intern_pool;
  36998     assert(slice_val.typeOf(zcu).isSlice(zcu));
  36999     const slice = switch (ip.indexToKey(slice_val.toIntern())) {
  37000         .undef => return sema.failWithUseOfUndef(block, src),
  37001         .slice => |slice| slice,
  37002         else => unreachable,
  37003     };
  37004     const elem_ty = Type.fromInterned(slice.ty).childType(zcu);
  37005     const len = try Value.fromInterned(slice.len).toUnsignedIntSema(pt);
  37006     const array_ty = try pt.arrayType(.{
  37007         .child = elem_ty.toIntern(),
  37008         .len = len,
  37009     });
  37010     const ptr_ty = try pt.ptrTypeSema(p: {
  37011         var p = Type.fromInterned(slice.ty).ptrInfo(zcu);
  37012         p.flags.size = .one;
  37013         p.child = array_ty.toIntern();
  37014         p.sentinel = .none;
  37015         break :p p;
  37016     });
  37017     const casted_ptr = try pt.getCoerced(Value.fromInterned(slice.ptr), ptr_ty);
  37018     return sema.pointerDeref(block, src, casted_ptr, ptr_ty);
  37019 }
  37020 
  37021 fn analyzeUnreachable(sema: *Sema, block: *Block, src: LazySrcLoc, safety_check: bool) !void {
  37022     if (safety_check and block.wantSafety()) {
  37023         // We only apply the first hint in a branch.
  37024         // This allows user-provided hints to override implicit cold hints.
  37025         if (sema.branch_hint == null) {
  37026             sema.branch_hint = .cold;
  37027         }
  37028 
  37029         try sema.safetyPanic(block, src, .reached_unreachable);
  37030     } else {
  37031         _ = try block.addNoOp(.unreach);
  37032     }
  37033 }
  37034 
  37035 /// This should be called exactly once, at the end of a `Sema`'s lifetime.
  37036 /// It takes the exports stored in `sema.export` and flushes them to the `Zcu`
  37037 /// to be processed by the linker after the update.
  37038 pub fn flushExports(sema: *Sema) !void {
  37039     if (sema.exports.items.len == 0) return;
  37040 
  37041     const zcu = sema.pt.zcu;
  37042     const gpa = zcu.gpa;
  37043 
  37044     // There may be existing exports. For instance, a struct may export
  37045     // things during both field type resolution and field default resolution.
  37046     //
  37047     // So, pick up and delete any existing exports. This strategy performs
  37048     // redundant work, but that's okay, because this case is exceedingly rare.
  37049     if (zcu.single_exports.get(sema.owner)) |export_idx| {
  37050         try sema.exports.append(gpa, export_idx.ptr(zcu).*);
  37051     } else if (zcu.multi_exports.get(sema.owner)) |info| {
  37052         try sema.exports.appendSlice(gpa, zcu.all_exports.items[info.index..][0..info.len]);
  37053     }
  37054     zcu.deleteUnitExports(sema.owner);
  37055 
  37056     // `sema.exports` is completed; store the data into the `Zcu`.
  37057     if (sema.exports.items.len == 1) {
  37058         try zcu.single_exports.ensureUnusedCapacity(gpa, 1);
  37059         const export_idx: Zcu.Export.Index = zcu.free_exports.pop() orelse idx: {
  37060             _ = try zcu.all_exports.addOne(gpa);
  37061             break :idx @enumFromInt(zcu.all_exports.items.len - 1);
  37062         };
  37063         export_idx.ptr(zcu).* = sema.exports.items[0];
  37064         zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx);
  37065     } else {
  37066         try zcu.multi_exports.ensureUnusedCapacity(gpa, 1);
  37067         const exports_base = zcu.all_exports.items.len;
  37068         try zcu.all_exports.appendSlice(gpa, sema.exports.items);
  37069         zcu.multi_exports.putAssumeCapacityNoClobber(sema.owner, .{
  37070             .index = @intCast(exports_base),
  37071             .len = @intCast(sema.exports.items.len),
  37072         });
  37073     }
  37074 }
  37075 
  37076 /// Called as soon as a `declared` enum type is created.
  37077 /// Resolves the tag type and field inits.
  37078 /// Marks the `src_inst` dependency on the enum's declaration, so call sites need not do this.
  37079 pub fn resolveDeclaredEnum(
  37080     pt: Zcu.PerThread,
  37081     wip_ty: InternPool.WipEnumType,
  37082     inst: Zir.Inst.Index,
  37083     tracked_inst: InternPool.TrackedInst.Index,
  37084     namespace: InternPool.NamespaceIndex,
  37085     type_name: InternPool.NullTerminatedString,
  37086     small: Zir.Inst.EnumDecl.Small,
  37087     body: []const Zir.Inst.Index,
  37088     tag_type_ref: Zir.Inst.Ref,
  37089     any_values: bool,
  37090     fields_len: u32,
  37091     zir: Zir,
  37092     body_end: usize,
  37093 ) Zcu.SemaError!void {
  37094     const zcu = pt.zcu;
  37095     const gpa = zcu.gpa;
  37096 
  37097     const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(.zero) };
  37098 
  37099     var arena: std.heap.ArenaAllocator = .init(gpa);
  37100     defer arena.deinit();
  37101 
  37102     var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa);
  37103     defer comptime_err_ret_trace.deinit();
  37104 
  37105     var sema: Sema = .{
  37106         .pt = pt,
  37107         .gpa = gpa,
  37108         .arena = arena.allocator(),
  37109         .code = zir,
  37110         .owner = .wrap(.{ .type = wip_ty.index }),
  37111         .func_index = .none,
  37112         .func_is_naked = false,
  37113         .fn_ret_ty = .void,
  37114         .fn_ret_ty_ies = null,
  37115         .comptime_err_ret_trace = &comptime_err_ret_trace,
  37116     };
  37117     defer sema.deinit();
  37118 
  37119     if (zcu.comp.debugIncremental()) {
  37120         const info = try zcu.incremental_debug_state.getUnitInfo(gpa, sema.owner);
  37121         info.last_update_gen = zcu.generation;
  37122     }
  37123 
  37124     try sema.declareDependency(.{ .src_hash = tracked_inst });
  37125 
  37126     var block: Block = .{
  37127         .parent = null,
  37128         .sema = &sema,
  37129         .namespace = namespace,
  37130         .instructions = .{},
  37131         .inlining = null,
  37132         .comptime_reason = .{ .reason = .{
  37133             .src = src,
  37134             .r = .{ .simple = .enum_fields },
  37135         } },
  37136         .src_base_inst = tracked_inst,
  37137         .type_name_ctx = type_name,
  37138     };
  37139     defer block.instructions.deinit(gpa);
  37140 
  37141     sema.resolveDeclaredEnumInner(
  37142         &block,
  37143         wip_ty,
  37144         inst,
  37145         tracked_inst,
  37146         src,
  37147         small,
  37148         body,
  37149         tag_type_ref,
  37150         any_values,
  37151         fields_len,
  37152         zir,
  37153         body_end,
  37154     ) catch |err| switch (err) {
  37155         error.ComptimeBreak => unreachable,
  37156         error.ComptimeReturn => unreachable,
  37157         error.OutOfMemory => |e| return e,
  37158         error.AnalysisFail => {
  37159             if (!zcu.failed_analysis.contains(sema.owner)) {
  37160                 try zcu.transitive_failed_analysis.put(gpa, sema.owner, {});
  37161             }
  37162             return error.AnalysisFail;
  37163         },
  37164     };
  37165 }
  37166 
  37167 fn resolveDeclaredEnumInner(
  37168     sema: *Sema,
  37169     block: *Block,
  37170     wip_ty: InternPool.WipEnumType,
  37171     inst: Zir.Inst.Index,
  37172     tracked_inst: InternPool.TrackedInst.Index,
  37173     src: LazySrcLoc,
  37174     small: Zir.Inst.EnumDecl.Small,
  37175     body: []const Zir.Inst.Index,
  37176     tag_type_ref: Zir.Inst.Ref,
  37177     any_values: bool,
  37178     fields_len: u32,
  37179     zir: Zir,
  37180     body_end: usize,
  37181 ) Zcu.CompileError!void {
  37182     const pt = sema.pt;
  37183     const zcu = pt.zcu;
  37184     const gpa = zcu.gpa;
  37185     const ip = &zcu.intern_pool;
  37186 
  37187     const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
  37188 
  37189     const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = .zero } };
  37190 
  37191     const int_tag_ty = ty: {
  37192         if (body.len != 0) {
  37193             _ = try sema.analyzeInlineBody(block, body, inst);
  37194         }
  37195 
  37196         if (tag_type_ref != .none) {
  37197             const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref);
  37198             if (ty.zigTypeTag(zcu) != .int and ty.zigTypeTag(zcu) != .comptime_int) {
  37199                 return sema.fail(block, tag_ty_src, "expected integer tag type, found '{f}'", .{ty.fmt(pt)});
  37200             }
  37201             break :ty ty;
  37202         } else if (fields_len == 0) {
  37203             break :ty try pt.intType(.unsigned, 0);
  37204         } else {
  37205             const bits = std.math.log2_int_ceil(usize, fields_len);
  37206             break :ty try pt.intType(.unsigned, bits);
  37207         }
  37208     };
  37209 
  37210     wip_ty.setTagTy(ip, int_tag_ty.toIntern());
  37211 
  37212     var extra_index = body_end + bit_bags_count;
  37213     var bit_bag_index: usize = body_end;
  37214     var cur_bit_bag: u32 = undefined;
  37215     var last_tag_val: ?Value = null;
  37216     for (0..fields_len) |field_i_usize| {
  37217         const field_i: u32 = @intCast(field_i_usize);
  37218         if (field_i % 32 == 0) {
  37219             cur_bit_bag = zir.extra[bit_bag_index];
  37220             bit_bag_index += 1;
  37221         }
  37222         const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0;
  37223         cur_bit_bag >>= 1;
  37224 
  37225         const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]);
  37226         const field_name_zir = zir.nullTerminatedString(field_name_index);
  37227         extra_index += 1; // field name
  37228 
  37229         const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
  37230 
  37231         const value_src: LazySrcLoc = .{
  37232             .base_node_inst = tracked_inst,
  37233             .offset = .{ .container_field_value = field_i },
  37234         };
  37235 
  37236         const tag_overflow = if (has_tag_value) overflow: {
  37237             const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
  37238             extra_index += 1;
  37239             const tag_inst = try sema.resolveInst(tag_val_ref);
  37240             last_tag_val = try sema.resolveConstDefinedValue(block, .{
  37241                 .base_node_inst = tracked_inst,
  37242                 .offset = .{ .container_field_name = field_i },
  37243             }, tag_inst, .{ .simple = .enum_field_tag_value });
  37244             if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
  37245             last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
  37246             if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
  37247                 assert(conflict.kind == .value); // AstGen validated names are unique
  37248                 const other_field_src: LazySrcLoc = .{
  37249                     .base_node_inst = tracked_inst,
  37250                     .offset = .{ .container_field_value = conflict.prev_field_idx },
  37251                 };
  37252                 const msg = msg: {
  37253                     const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
  37254                     errdefer msg.destroy(gpa);
  37255                     try sema.errNote(other_field_src, msg, "other occurrence here", .{});
  37256                     break :msg msg;
  37257                 };
  37258                 return sema.failWithOwnedErrorMsg(block, msg);
  37259             }
  37260             break :overflow false;
  37261         } else if (any_values) overflow: {
  37262             if (last_tag_val) |last_tag| {
  37263                 const result = try arith.incrementDefinedInt(sema, int_tag_ty, last_tag);
  37264                 last_tag_val = result.val;
  37265                 if (result.overflow) break :overflow true;
  37266             } else {
  37267                 last_tag_val = try pt.intValue(int_tag_ty, 0);
  37268             }
  37269             if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
  37270                 assert(conflict.kind == .value); // AstGen validated names are unique
  37271                 const other_field_src: LazySrcLoc = .{
  37272                     .base_node_inst = tracked_inst,
  37273                     .offset = .{ .container_field_value = conflict.prev_field_idx },
  37274                 };
  37275                 const msg = msg: {
  37276                     const msg = try sema.errMsg(value_src, "enum tag value {f} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
  37277                     errdefer msg.destroy(gpa);
  37278                     try sema.errNote(other_field_src, msg, "other occurrence here", .{});
  37279                     break :msg msg;
  37280                 };
  37281                 return sema.failWithOwnedErrorMsg(block, msg);
  37282             }
  37283             break :overflow false;
  37284         } else overflow: {
  37285             assert(wip_ty.nextField(ip, field_name, .none) == null);
  37286             last_tag_val = try pt.intValue(.comptime_int, field_i);
  37287             if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
  37288             last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
  37289             break :overflow false;
  37290         };
  37291 
  37292         if (tag_overflow) {
  37293             const msg = try sema.errMsg(value_src, "enumeration value '{f}' too large for type '{f}'", .{
  37294                 last_tag_val.?.fmtValueSema(pt, sema), int_tag_ty.fmt(pt),
  37295             });
  37296             return sema.failWithOwnedErrorMsg(block, msg);
  37297         }
  37298     }
  37299     if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
  37300         if (fields_len >= 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(zcu)) {
  37301             return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
  37302         }
  37303     }
  37304 }
  37305 
  37306 pub const bitCastVal = @import("Sema/bitcast.zig").bitCast;
  37307 pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice;
  37308 
  37309 const loadComptimePtr = @import("Sema/comptime_ptr_access.zig").loadComptimePtr;
  37310 const ComptimeLoadResult = @import("Sema/comptime_ptr_access.zig").ComptimeLoadResult;
  37311 const storeComptimePtr = @import("Sema/comptime_ptr_access.zig").storeComptimePtr;
  37312 const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStoreResult;
  37313 
  37314 pub fn getBuiltinType(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!Type {
  37315     assert(decl.kind() == .type);
  37316     try sema.ensureMemoizedStateResolved(src, decl.stage());
  37317     return .fromInterned(sema.pt.zcu.builtin_decl_values.get(decl));
  37318 }
  37319 pub fn getBuiltin(sema: *Sema, src: LazySrcLoc, decl: Zcu.BuiltinDecl) SemaError!InternPool.Index {
  37320     assert(decl.kind() != .type);
  37321     try sema.ensureMemoizedStateResolved(src, decl.stage());
  37322     return sema.pt.zcu.builtin_decl_values.get(decl);
  37323 }
  37324 
  37325 pub const NavPtrModifiers = struct {
  37326     alignment: Alignment,
  37327     @"linksection": InternPool.OptionalNullTerminatedString,
  37328     @"addrspace": std.builtin.AddressSpace,
  37329 };
  37330 
  37331 pub fn resolveNavPtrModifiers(
  37332     sema: *Sema,
  37333     block: *Block,
  37334     zir_decl: Zir.Inst.Declaration.Unwrapped,
  37335     decl_inst: Zir.Inst.Index,
  37336     nav_ty: Type,
  37337 ) CompileError!NavPtrModifiers {
  37338     const pt = sema.pt;
  37339     const zcu = pt.zcu;
  37340     const gpa = zcu.gpa;
  37341     const ip = &zcu.intern_pool;
  37342 
  37343     const align_src = block.src(.{ .node_offset_var_decl_align = .zero });
  37344     const section_src = block.src(.{ .node_offset_var_decl_section = .zero });
  37345     const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = .zero });
  37346 
  37347     const alignment: InternPool.Alignment = a: {
  37348         const align_body = zir_decl.align_body orelse break :a .none;
  37349         const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst);
  37350         break :a try sema.analyzeAsAlign(block, align_src, align_ref);
  37351     };
  37352 
  37353     const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
  37354         const linksection_body = zir_decl.linksection_body orelse break :ls .none;
  37355         const linksection_ref = try sema.resolveInlineBody(block, linksection_body, decl_inst);
  37356         const bytes = try sema.toConstString(block, section_src, linksection_ref, .{ .simple = .@"linksection" });
  37357         if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
  37358             return sema.fail(block, section_src, "linksection cannot contain null bytes", .{});
  37359         } else if (bytes.len == 0) {
  37360             return sema.fail(block, section_src, "linksection cannot be empty", .{});
  37361         }
  37362         break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
  37363     };
  37364 
  37365     const @"addrspace": std.builtin.AddressSpace = as: {
  37366         const addrspace_ctx: std.builtin.AddressSpace.Context = switch (zir_decl.kind) {
  37367             .@"var" => .variable,
  37368             else => switch (nav_ty.zigTypeTag(zcu)) {
  37369                 .@"fn" => .function,
  37370                 else => .constant,
  37371             },
  37372         };
  37373         const target = zcu.getTarget();
  37374         const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {
  37375             .function => target_util.defaultAddressSpace(target, .function),
  37376             .variable => target_util.defaultAddressSpace(target, .global_mutable),
  37377             .constant => target_util.defaultAddressSpace(target, .global_constant),
  37378             else => unreachable,
  37379         };
  37380         const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst);
  37381         break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx);
  37382     };
  37383 
  37384     return .{
  37385         .alignment = alignment,
  37386         .@"linksection" = @"linksection",
  37387         .@"addrspace" = @"addrspace",
  37388     };
  37389 }
  37390 
  37391 pub fn analyzeMemoizedState(sema: *Sema, block: *Block, simple_src: LazySrcLoc, builtin_namespace: InternPool.NamespaceIndex, stage: InternPool.MemoizedStateStage) CompileError!bool {
  37392     const pt = sema.pt;
  37393     const zcu = pt.zcu;
  37394     const ip = &zcu.intern_pool;
  37395     const gpa = zcu.gpa;
  37396 
  37397     var any_changed = false;
  37398 
  37399     inline for (comptime std.enums.values(Zcu.BuiltinDecl)) |builtin_decl| {
  37400         if (stage == comptime builtin_decl.stage()) {
  37401             const parent_ns: Zcu.Namespace.Index, const parent_name: []const u8, const name: []const u8 = switch (comptime builtin_decl.access()) {
  37402                 .direct => |name| .{ builtin_namespace, "std.builtin", name },
  37403                 .nested => |nested| access: {
  37404                     const parent_ty: Type = .fromInterned(zcu.builtin_decl_values.get(nested[0]));
  37405                     const parent_ns = parent_ty.getNamespace(zcu).unwrap() orelse {
  37406                         return sema.fail(block, simple_src, "std.builtin.{s} is not a container type", .{@tagName(nested[0])});
  37407                     };
  37408                     break :access .{ parent_ns, "std.builtin." ++ @tagName(nested[0]), nested[1] };
  37409                 },
  37410             };
  37411 
  37412             const name_nts = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
  37413             const nav = try sema.namespaceLookup(block, simple_src, parent_ns, name_nts) orelse
  37414                 return sema.fail(block, simple_src, "{s} missing {s}", .{ parent_name, name });
  37415 
  37416             const src: LazySrcLoc = .{
  37417                 .base_node_inst = ip.getNav(nav).srcInst(ip),
  37418                 .offset = .nodeOffset(.zero),
  37419             };
  37420 
  37421             const result = try sema.analyzeNavVal(block, src, nav);
  37422 
  37423             const uncoerced_val = try sema.resolveConstDefinedValue(block, src, result, null);
  37424             const maybe_lazy_val: Value = switch (builtin_decl.kind()) {
  37425                 .type => if (uncoerced_val.typeOf(zcu).zigTypeTag(zcu) != .type) {
  37426                     return sema.fail(block, src, "{s}.{s} is not a type", .{ parent_name, name });
  37427                 } else val: {
  37428                     try uncoerced_val.toType().resolveFully(pt);
  37429                     break :val uncoerced_val;
  37430                 },
  37431                 .func => val: {
  37432                     const func_ty = try sema.getExpectedBuiltinFnType(builtin_decl);
  37433                     const coerced = try sema.coerce(block, func_ty, Air.internedToRef(uncoerced_val.toIntern()), src);
  37434                     break :val .fromInterned(coerced.toInterned().?);
  37435                 },
  37436                 .string => val: {
  37437                     const coerced = try sema.coerce(block, .slice_const_u8, Air.internedToRef(uncoerced_val.toIntern()), src);
  37438                     break :val .fromInterned(coerced.toInterned().?);
  37439                 },
  37440             };
  37441             const val = try sema.resolveLazyValue(maybe_lazy_val);
  37442 
  37443             const prev = zcu.builtin_decl_values.get(builtin_decl);
  37444             if (val.toIntern() != prev) {
  37445                 zcu.builtin_decl_values.set(builtin_decl, val.toIntern());
  37446                 any_changed = true;
  37447             }
  37448         }
  37449     }
  37450 
  37451     return any_changed;
  37452 }
  37453 
  37454 /// Given that `decl.kind() == .func`, get the type expected of the function.
  37455 fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Type {
  37456     const pt = sema.pt;
  37457     return switch (decl) {
  37458         // `noinline fn () void`
  37459         .returnError => try pt.funcType(.{
  37460             .param_types = &.{},
  37461             .return_type = .void_type,
  37462             .is_noinline = true,
  37463         }),
  37464 
  37465         // `fn ([]const u8, ?usize) noreturn`
  37466         .@"panic.call" => try pt.funcType(.{
  37467             .param_types = &.{
  37468                 .slice_const_u8_type,
  37469                 (try pt.optionalType(.usize_type)).toIntern(),
  37470             },
  37471             .return_type = .noreturn_type,
  37472         }),
  37473 
  37474         // `fn (anytype, anytype) noreturn`
  37475         .@"panic.sentinelMismatch",
  37476         .@"panic.inactiveUnionField",
  37477         => try pt.funcType(.{
  37478             .param_types = &.{ .generic_poison_type, .generic_poison_type },
  37479             .return_type = .noreturn_type,
  37480             .is_generic = true,
  37481         }),
  37482 
  37483         // `fn (anyerror) noreturn`
  37484         .@"panic.unwrapError" => try pt.funcType(.{
  37485             .param_types = &.{.anyerror_type},
  37486             .return_type = .noreturn_type,
  37487         }),
  37488 
  37489         // `fn (usize) noreturn`
  37490         .@"panic.sliceCastLenRemainder" => try pt.funcType(.{
  37491             .param_types = &.{.usize_type},
  37492             .return_type = .noreturn_type,
  37493         }),
  37494 
  37495         // `fn (usize, usize) noreturn`
  37496         .@"panic.outOfBounds",
  37497         .@"panic.startGreaterThanEnd",
  37498         => try pt.funcType(.{
  37499             .param_types = &.{ .usize_type, .usize_type },
  37500             .return_type = .noreturn_type,
  37501         }),
  37502 
  37503         // `fn () noreturn`
  37504         .@"panic.reachedUnreachable",
  37505         .@"panic.unwrapNull",
  37506         .@"panic.castToNull",
  37507         .@"panic.incorrectAlignment",
  37508         .@"panic.invalidErrorCode",
  37509         .@"panic.integerOutOfBounds",
  37510         .@"panic.integerOverflow",
  37511         .@"panic.shlOverflow",
  37512         .@"panic.shrOverflow",
  37513         .@"panic.divideByZero",
  37514         .@"panic.exactDivisionRemainder",
  37515         .@"panic.integerPartOutOfBounds",
  37516         .@"panic.corruptSwitch",
  37517         .@"panic.shiftRhsTooBig",
  37518         .@"panic.invalidEnumValue",
  37519         .@"panic.forLenMismatch",
  37520         .@"panic.copyLenMismatch",
  37521         .@"panic.memcpyAlias",
  37522         .@"panic.noreturnReturned",
  37523         => try pt.funcType(.{
  37524             .param_types = &.{},
  37525             .return_type = .noreturn_type,
  37526         }),
  37527 
  37528         else => unreachable,
  37529     };
  37530 }