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
8const std = @import("std");
9const math = std.math;
10const mem = std.mem;
11const Allocator = mem.Allocator;
12const assert = std.debug.assert;
13const log = std.log.scoped(.sema);
14
15const Sema = @This();
16const Value = @import("Value.zig");
17const MutableValue = @import("mutable_value.zig").MutableValue;
18const Type = @import("Type.zig");
19const Air = @import("Air.zig");
20const Zir = std.zig.Zir;
21const Zcu = @import("Zcu.zig");
22const Namespace = Zcu.Namespace;
23const CompileError = Zcu.CompileError;
24const SemaError = Zcu.SemaError;
25const LazySrcLoc = Zcu.LazySrcLoc;
26const RangeSet = @import("RangeSet.zig");
27const target_util = @import("target.zig");
28const crash_report = @import("crash_report.zig");
29const build_options = @import("build_options");
30const Compilation = @import("Compilation.zig");
31const InternPool = @import("InternPool.zig");
32const Alignment = InternPool.Alignment;
33const AnalUnit = InternPool.AnalUnit;
34const ComptimeAllocIndex = InternPool.ComptimeAllocIndex;
35const Cache = std.Build.Cache;
36const LowerZon = @import("Sema/LowerZon.zig");
37const arith = @import("Sema/arith.zig");
38const Module = @import("Module.zig");
39
40pt: Zcu.PerThread,
41/// Alias to `zcu.gpa`.
42gpa: Allocator,
43/// Points to the temporary arena allocator of the Sema.
44/// This arena will be cleared when the sema is destroyed.
45arena: Allocator,
46code: Zir,
47air_instructions: std.MultiArrayList(Air.Inst) = .{},
48air_extra: std.ArrayList(u32) = .empty,
49/// Maps ZIR to AIR.
50inst_map: InstMap = .{},
51/// The "owner" of a `Sema` represents the root "thing" that is being analyzed.
52/// This does not change throughout the entire lifetime of a `Sema`. For instance,
53/// when analyzing a runtime function body, this is always `func` of that function,
54/// even if an inline/comptime function call is being analyzed.
55owner: AnalUnit,
56/// The function this ZIR code is the body of, according to the source code.
57/// This starts out the same as `sema.owner.func` if applicable, and then diverges
58/// in the case of an inline or comptime function call.
59/// This could be `none`, a `func_decl`, or a `func_instance`.
60func_index: InternPool.Index,
61/// Whether the type of func_index has a calling convention of `.naked`.
62func_is_naked: bool,
63/// Used to restore the error return trace when returning a non-error from a function.
64error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none,
65comptime_err_ret_trace: *std.array_list.Managed(LazySrcLoc),
66/// When semantic analysis needs to know the return type of the function whose body
67/// is being analyzed, this `Type` should be used instead of going through `func`.
68/// This will correctly handle the case of a comptime/inline function call of a
69/// generic function which uses a type expression for the return type.
70/// The type will be `void` in the case that `func` is `null`.
71fn_ret_ty: Type,
72/// In case of the return type being an error union with an inferred error
73/// set, this is the inferred error set. `null` otherwise. Allocated with
74/// `Sema.arena`.
75fn_ret_ty_ies: ?*InferredErrorSet,
76branch_quota: u32 = default_branch_quota,
77branch_count: u32 = 0,
78/// Populated when returning `error.ComptimeBreak`. Used to communicate the
79/// break instruction up the stack to find the corresponding Block.
80comptime_break_inst: Zir.Inst.Index = undefined,
81/// These are lazily created runtime blocks from block_inline instructions.
82/// They are created when an break_inline passes through a runtime condition, because
83/// Sema must convert comptime control flow to runtime control flow, which means
84/// breaking from a block.
85post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .empty,
86/// Populated with the last compile error created.
87err: ?*Zcu.ErrorMsg = null,
88
89/// The temporary arena is used for the memory of the `InferredAlloc` values
90/// here so the values can be dropped without any cleanup.
91unresolved_inferred_allocs: std.array_hash_map.Auto(Air.Inst.Index, InferredAlloc) = .empty,
92
93/// Links every pointer derived from a base `alloc` back to that `alloc`. Used
94/// to detect comptime-known `const`s.
95/// TODO: ZIR liveness analysis would allow us to remove elements from this map.
96base_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, Air.Inst.Index) = .empty,
97
98/// Runtime `alloc`s are placed in this map to track all comptime-known writes
99/// before the corresponding `make_ptr_const` instruction.
100/// If any store to the alloc depends on a runtime condition or stores a runtime
101/// value, the corresponding element in this map is erased, to indicate that the
102/// alloc is not comptime-known.
103/// If the alloc remains in this map when `make_ptr_const` is reached, its value
104/// is comptime-known, and all stores to the pointer must be applied at comptime
105/// to determine the comptime value.
106/// Backed by gpa.
107maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAlloc) = .empty,
108
109/// Comptime-mutable allocs, and any comptime allocs which reference it, are
110/// stored as elements of this array.
111/// Pointers to such memory are represented via an index into this array.
112/// Backed by gpa.
113comptime_allocs: std.ArrayList(ComptimeAlloc) = .empty,
114
115/// A list of exports performed by this analysis. After this `Sema` terminates,
116/// these are flushed to `Zcu.single_exports` or `Zcu.multi_exports`.
117exports: std.ArrayList(Zcu.Export) = .empty,
118
119/// All references registered so far by this `Sema`. This is a temporary duplicate
120/// of data stored in `Zcu.all_references`. It exists to avoid adding references to
121/// a given `AnalUnit` multiple times.
122references: std.array_hash_map.Auto(AnalUnit, void) = .empty,
123type_references: std.array_hash_map.Auto(InternPool.Index, void) = .empty,
124
125/// All dependencies registered so far by this `Sema`. This is a temporary duplicate
126/// of the main dependency data. It exists to avoid adding dependencies to a given
127/// `AnalUnit` multiple times.
128dependencies: std.array_hash_map.Auto(InternPool.Dependee, void) = .empty,
129
130/// Whether memoization of this call is permitted. Operations with side effects global
131/// to the `Sema`, such as `@setEvalBranchQuota`, set this to `false`. It is observed
132/// by `analyzeCall`.
133allow_memoize: bool = true,
134
135/// The largest quota requested by `@setEvalBranchQuota` within the comptime call
136/// currently being analyzed.
137quota_request: u32 = 0,
138
139/// The `BranchHint` for the current branch of runtime control flow.
140/// This state is on `Sema` so that `cold` hints can be propagated up through blocks with less special handling.
141branch_hint: ?std.lang.BranchHint = null,
142
143const RuntimeIndex = enum(u32) {
144 zero = 0,
145 comptime_field_ptr = std.math.maxInt(u32),
146 _,
147
148 pub fn increment(ri: *RuntimeIndex) void {
149 ri.* = @fromBackingInt(@intCast(@backingInt(ri.*) + 1));
150 }
151};
152
153const MaybeComptimeAlloc = struct {
154 /// The runtime index of the `alloc` instruction.
155 runtime_index: RuntimeIndex,
156 /// Backed by sema.arena. Tracks all comptime-known stores to this `alloc`. Due to
157 /// RLS, a single comptime-known allocation may have arbitrarily many stores.
158 /// This list also contains `set_union_tag`, `optional_payload_ptr_set`, and
159 /// `errunion_payload_ptr_set` instructions.
160 /// If the instruction is one of these three tags, `src` may be `.unneeded`.
161 stores: std.MultiArrayList(struct {
162 inst: Air.Inst.Index,
163 src: LazySrcLoc,
164 }) = .{},
165};
166
167const ComptimeAlloc = struct {
168 val: MutableValue,
169 is_const: bool,
170 src: LazySrcLoc,
171 /// `.none` indicates that the alignment is the natural alignment of `val`.
172 alignment: Alignment,
173 /// This is the `runtime_index` at the point of this allocation. If an store
174 /// to this alloc ever occurs with a runtime index greater than this one, it
175 /// is behind a runtime condition, so a compile error will be emitted.
176 runtime_index: RuntimeIndex,
177};
178
179/// Asserts that `ty` is not an OPV type.
180/// `src` may be `null` if `is_const` will be set.
181fn newComptimeAlloc(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, alignment: Alignment) !ComptimeAllocIndex {
182 const pt = sema.pt;
183
184 switch (ty.classify(pt.zcu)) {
185 .no_possible_value => unreachable,
186 .one_possible_value => unreachable,
187 else => {},
188 }
189
190 const idx = sema.comptime_allocs.items.len;
191 try sema.comptime_allocs.append(sema.gpa, .{
192 .val = .{ .interned = (try pt.undefValue(ty)).toIntern() },
193 .is_const = false,
194 .src = src,
195 .alignment = alignment,
196 .runtime_index = block.runtime_index,
197 });
198 return @fromBackingInt(@intCast(idx));
199}
200
201pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc {
202 return &sema.comptime_allocs.items[@backingInt(idx)];
203}
204
205pub const default_branch_quota = 1000;
206
207pub const InferredErrorSet = struct {
208 /// The function body from which this error set originates.
209 /// This is `none` in the case of a comptime/inline function call, corresponding to
210 /// `InternPool.Index.adhoc_inferred_error_set_type`.
211 /// The function's resolved error set is not set until analysis of the
212 /// function body completes.
213 func: InternPool.Index,
214 /// All currently known errors that this error set contains. This includes
215 /// direct additions via `return error.Foo;`, and possibly also errors that
216 /// are returned from any dependent functions.
217 errors: NameMap = .{},
218 /// Other inferred error sets which this inferred error set should include.
219 inferred_error_sets: std.array_hash_map.Auto(InternPool.Index, void) = .empty,
220 /// The regular error set created by resolving this inferred error set.
221 resolved: InternPool.Index = .none,
222
223 pub const NameMap = std.array_hash_map.Auto(InternPool.NullTerminatedString, void);
224
225 pub fn addErrorSet(
226 self: *InferredErrorSet,
227 err_set_ty: Type,
228 ip: *InternPool,
229 arena: Allocator,
230 ) !void {
231 switch (err_set_ty.toIntern()) {
232 .anyerror_type => self.resolved = .anyerror_type,
233 .adhoc_inferred_error_set_type => {}, // Adding an inferred error set to itself.
234
235 else => switch (ip.indexToKey(err_set_ty.toIntern())) {
236 .error_set_type => |error_set_type| {
237 for (error_set_type.names.get(ip)) |name| {
238 try self.errors.put(arena, name, {});
239 }
240 },
241 .inferred_error_set_type => {
242 try self.inferred_error_sets.put(arena, err_set_ty.toIntern(), {});
243 },
244 else => unreachable,
245 },
246 }
247 }
248};
249
250/// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve
251/// instructions during analysis.
252/// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the
253/// zir instruction index and a start offset. An index is not present in the map if the value
254/// at the index is `Air.Inst.Ref.none`.
255/// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that
256/// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can
257/// be called safely for any of the instructions passed in.
258pub const InstMap = struct {
259 items: []Air.Inst.Ref = &[_]Air.Inst.Ref{},
260 start: Zir.Inst.Index = @fromBackingInt(@intCast(0)),
261
262 pub fn deinit(map: InstMap, allocator: mem.Allocator) void {
263 allocator.free(map.items);
264 }
265
266 pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref {
267 if (!map.contains(key)) return null;
268 return map.items[@backingInt(key) - @backingInt(map.start)];
269 }
270
271 pub fn putAssumeCapacity(
272 map: *InstMap,
273 key: Zir.Inst.Index,
274 ref: Air.Inst.Ref,
275 ) void {
276 map.items[@backingInt(key) - @backingInt(map.start)] = ref;
277 }
278
279 pub fn putAssumeCapacityNoClobber(
280 map: *InstMap,
281 key: Zir.Inst.Index,
282 ref: Air.Inst.Ref,
283 ) void {
284 assert(!map.contains(key));
285 map.putAssumeCapacity(key, ref);
286 }
287
288 pub const GetOrPutResult = struct {
289 value_ptr: *Air.Inst.Ref,
290 found_existing: bool,
291 };
292
293 pub fn getOrPutAssumeCapacity(
294 map: *InstMap,
295 key: Zir.Inst.Index,
296 ) GetOrPutResult {
297 const index = @backingInt(key) - @backingInt(map.start);
298 return GetOrPutResult{
299 .value_ptr = &map.items[index],
300 .found_existing = map.items[index] != .none,
301 };
302 }
303
304 pub fn remove(map: InstMap, key: Zir.Inst.Index) bool {
305 if (!map.contains(key)) return false;
306 map.items[@backingInt(key) - @backingInt(map.start)] = .none;
307 return true;
308 }
309
310 pub fn contains(map: InstMap, key: Zir.Inst.Index) bool {
311 return map.items[@backingInt(key) - @backingInt(map.start)] != .none;
312 }
313
314 pub fn ensureSpaceForInstructions(
315 map: *InstMap,
316 allocator: mem.Allocator,
317 insts: []const Zir.Inst.Index,
318 ) !void {
319 const start, const end = mem.minMax(u32, @ptrCast(insts));
320 const map_start = @backingInt(map.start);
321 if (map_start <= start and end < map.items.len + map_start)
322 return;
323
324 const old_start = if (map.items.len == 0) start else map_start;
325 var better_capacity = map.items.len;
326 var better_start = old_start;
327 while (true) {
328 const extra_capacity = better_capacity / 2 + 16;
329 better_capacity += extra_capacity;
330 better_start -|= @intCast(extra_capacity / 2);
331 if (better_start <= start and end < better_capacity + better_start)
332 break;
333 }
334
335 const start_diff = old_start - better_start;
336 const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity);
337 @memset(new_items[0..start_diff], .none);
338 @memcpy(new_items[start_diff..][0..map.items.len], map.items);
339 @memset(new_items[start_diff + map.items.len ..], .none);
340
341 allocator.free(map.items);
342 map.items = new_items;
343 map.start = @fromBackingInt(@intCast(better_start));
344 }
345};
346
347/// This is the context needed to semantically analyze ZIR instructions and
348/// produce AIR instructions.
349/// This is a temporary structure stored on the stack; references to it are valid only
350/// during semantic analysis of the block.
351pub const Block = struct {
352 parent: ?*Block,
353 /// Shared among all child blocks.
354 sema: *Sema,
355 /// The namespace to use for lookups from this source block
356 namespace: InternPool.NamespaceIndex,
357 /// The AIR instructions generated for this block.
358 instructions: std.ArrayList(Air.Inst.Index),
359 // `param` instructions are collected here to be used by the `func` instruction.
360 /// When doing a generic function instantiation, this array collects a type
361 /// for each *runtime-known* parameter. This array corresponds to the instance
362 /// function type, while `Sema.comptime_args` corresponds to the generic owner
363 /// function type.
364 /// This memory is allocated by a parent `Sema` in the temporary arena, and is
365 /// used to add a `func_instance` into the `InternPool`.
366 params: std.MultiArrayList(Param) = .{},
367
368 label: ?*Label = null,
369 inlining: ?*Inlining,
370 /// If runtime_index is not 0 then one of these is guaranteed to be non null.
371 runtime_cond: ?LazySrcLoc = null,
372 runtime_loop: ?LazySrcLoc = null,
373 /// Non zero if a non-inline loop or a runtime conditional have been encountered.
374 /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index.
375 runtime_index: RuntimeIndex = .zero,
376 inline_block: Zir.Inst.OptionalIndex = .none,
377
378 comptime_reason: ?BlockComptimeReason = null,
379 is_typeof: bool = false,
380
381 /// Keep track of the active error return trace index around blocks so that we can correctly
382 /// pop the error trace upon block exit.
383 error_return_trace_index: Air.Inst.Ref = .none,
384
385 /// when null, it is determined by the owner modules build mode, changed by @setRuntimeSafety
386 want_safety: ?bool = null,
387
388 /// What mode to generate float operations in, set by @setFloatMode
389 float_mode: std.lang.FloatMode = .strict,
390
391 /// If not `null`, this boolean is set when a `dbg_var_ptr`, `dbg_var_val`, or `dbg_arg_inline`.
392 /// instruction is emitted. It signals that the innermost lexically
393 /// enclosing `block`/`block_inline` should be translated into a real AIR
394 /// `block` in order for codegen to match lexical scoping for debug vars.
395 need_debug_scope: ?*bool = null,
396
397 /// Relative source locations encountered while traversing this block should be
398 /// treated as relative to the AST node of this ZIR instruction.
399 src_base_inst: InternPool.TrackedInst.Index,
400
401 /// The name of the current "context" for naming namespace types.
402 /// The interpretation of this depends on the name strategy in ZIR, but the name
403 /// is always incorporated into the type name somehow.
404 /// See `Sema.setTypeName`.
405 type_name_ctx: InternPool.NullTerminatedString,
406 type_fqn_ctx: InternPool.NullTerminatedString,
407
408 /// Create a `LazySrcLoc` based on an `Offset` from the code being analyzed in this block.
409 /// Specifically, the given `Offset` is treated as relative to `block.src_base_inst`.
410 pub fn src(block: Block, offset: LazySrcLoc.Offset) LazySrcLoc {
411 return .{
412 .base_node_inst = block.src_base_inst,
413 .offset = offset,
414 };
415 }
416
417 fn isComptime(block: Block) bool {
418 return block.comptime_reason != null;
419 }
420
421 pub fn builtinCallArgSrc(block: *Block, builtin_call_node: std.zig.Ast.Node.Offset, arg_index: u32) LazySrcLoc {
422 return block.src(.{ .node_offset_builtin_call_arg = .{
423 .builtin_call_node = builtin_call_node,
424 .arg_index = arg_index,
425 } });
426 }
427
428 pub fn nodeOffset(block: Block, node_offset: std.zig.Ast.Node.Offset) LazySrcLoc {
429 return block.src(LazySrcLoc.Offset.nodeOffset(node_offset));
430 }
431
432 fn tokenOffset(block: Block, tok_offset: std.zig.Ast.TokenOffset) LazySrcLoc {
433 return block.src(.{ .token_offset = tok_offset });
434 }
435
436 const Param = struct {
437 /// `none` means `anytype`.
438 ty: InternPool.Index,
439 is_comptime: bool,
440 name: Zir.NullTerminatedString,
441 };
442
443 /// This `Block` maps a block ZIR instruction to the corresponding
444 /// AIR instruction for break instruction analysis.
445 pub const Label = struct {
446 zir_block: Zir.Inst.Index,
447 merges: Merges,
448 };
449
450 /// This `Block` indicates that an inline function call is happening
451 /// and return instructions should be analyzed as a break instruction
452 /// to this AIR block instruction.
453 /// It is shared among all the blocks in an inline or comptime called
454 /// function.
455 pub const Inlining = struct {
456 call_block: *Block,
457 call_src: LazySrcLoc,
458 func: InternPool.Index,
459
460 /// Populated lazily by `refFrame`.
461 ref_frame: Zcu.InlineReferenceFrame.Index.Optional = .none,
462
463 /// If `true`, the following fields are `undefined`. This doesn't represent a true inline
464 /// call, but rather a generic call analyzing the instantiation's generic type bodies.
465 is_generic_instantiation: bool,
466
467 has_comptime_args: bool,
468 comptime_result: Air.Inst.Ref,
469 merges: Merges,
470
471 fn refFrame(inlining: *Inlining, zcu: *Zcu) Allocator.Error!Zcu.InlineReferenceFrame.Index {
472 if (inlining.ref_frame == .none) {
473 inlining.ref_frame = (try zcu.addInlineReferenceFrame(.{
474 .callee = inlining.func,
475 .call_src = inlining.call_src,
476 .parent = if (inlining.call_block.inlining) |parent_inlining| p: {
477 break :p (try parent_inlining.refFrame(zcu)).toOptional();
478 } else .none,
479 })).toOptional();
480 }
481 return inlining.ref_frame.unwrap().?;
482 }
483 };
484
485 pub const Merges = struct {
486 block_inst: Air.Inst.Index,
487 /// Separate array list from break_inst_list so that it can be passed directly
488 /// to resolvePeerTypes.
489 results: std.ArrayList(Air.Inst.Ref),
490 /// Keeps track of the break instructions so that the operand can be replaced
491 /// if we need to add type coercion at the end of block analysis.
492 /// Same indexes, capacity, length as `results`.
493 br_list: std.ArrayList(Air.Inst.Index),
494 /// Keeps the source location of the rhs operand of the break instruction,
495 /// to enable more precise compile errors.
496 /// Same indexes, capacity, length as `results`.
497 src_locs: std.ArrayList(?LazySrcLoc),
498 /// Most blocks do not utilize this field. When it is used, its use is
499 /// contextual. The possible uses are as follows:
500 /// * for a `switch_block[_ref]`, this refers to dummy `br` instructions
501 /// which correspond to `switch_continue` ZIR. The switch logic will
502 /// rewrite these to appropriate AIR switch dispatches.
503 extra_insts: std.ArrayList(Air.Inst.Index) = .empty,
504 /// Same indexes, capacity, length as `extra_insts`.
505 extra_src_locs: std.ArrayList(LazySrcLoc) = .empty,
506
507 pub fn deinit(merges: *@This(), allocator: Allocator) void {
508 merges.results.deinit(allocator);
509 merges.br_list.deinit(allocator);
510 merges.src_locs.deinit(allocator);
511 merges.extra_insts.deinit(allocator);
512 merges.extra_src_locs.deinit(allocator);
513 }
514 };
515
516 pub fn makeSubBlock(parent: *Block) Block {
517 return .{
518 .parent = parent,
519 .sema = parent.sema,
520 .namespace = parent.namespace,
521 .instructions = .empty,
522 .label = null,
523 .inlining = parent.inlining,
524 .comptime_reason = parent.comptime_reason,
525 .is_typeof = parent.is_typeof,
526 .runtime_cond = parent.runtime_cond,
527 .runtime_loop = parent.runtime_loop,
528 .runtime_index = parent.runtime_index,
529 .want_safety = parent.want_safety,
530 .float_mode = parent.float_mode,
531 .error_return_trace_index = parent.error_return_trace_index,
532 .need_debug_scope = parent.need_debug_scope,
533 .src_base_inst = parent.src_base_inst,
534 .type_name_ctx = parent.type_name_ctx,
535 .type_fqn_ctx = parent.type_fqn_ctx,
536 };
537 }
538
539 fn wantSafeTypes(block: *const Block) bool {
540 return block.want_safety orelse switch (block.ownerModule().optimize_mode) {
541 .debug => true,
542 .safe => true,
543 .fast => false,
544 .small => false,
545 };
546 }
547
548 fn wantSafety(block: *const Block) bool {
549 if (block.isComptime()) return false; // runtime safety checks are pointless in comptime blocks
550 return block.want_safety orelse switch (block.ownerModule().optimize_mode) {
551 .debug => true,
552 .safe => true,
553 .fast => false,
554 .small => false,
555 };
556 }
557
558 pub fn getFileScope(block: *Block, zcu: *Zcu) *Zcu.File {
559 return zcu.fileByIndex(getFileScopeIndex(block, zcu));
560 }
561
562 pub fn getFileScopeIndex(block: *Block, zcu: *Zcu) Zcu.File.Index {
563 return zcu.namespacePtr(block.namespace).file_scope;
564 }
565
566 fn addTy(
567 block: *Block,
568 tag: Air.Inst.Tag,
569 ty: Type,
570 ) error{OutOfMemory}!Air.Inst.Ref {
571 return block.addInst(.{
572 .tag = tag,
573 .data = .{ .ty = ty },
574 });
575 }
576
577 fn addTyOp(
578 block: *Block,
579 tag: Air.Inst.Tag,
580 ty: Type,
581 operand: Air.Inst.Ref,
582 ) error{OutOfMemory}!Air.Inst.Ref {
583 return block.addInst(.{
584 .tag = tag,
585 .data = .{ .ty_op = .{
586 .ty = ty,
587 .operand = operand,
588 } },
589 });
590 }
591
592 fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref {
593 return block.addInst(.{
594 .tag = tag,
595 .data = .{ .no_op = {} },
596 });
597 }
598
599 fn addUnOp(
600 block: *Block,
601 tag: Air.Inst.Tag,
602 operand: Air.Inst.Ref,
603 ) error{OutOfMemory}!Air.Inst.Ref {
604 return block.addInst(.{
605 .tag = tag,
606 .data = .{ .un_op = operand },
607 });
608 }
609
610 fn addBr(
611 block: *Block,
612 target_block: Air.Inst.Index,
613 operand: Air.Inst.Ref,
614 ) error{OutOfMemory}!Air.Inst.Ref {
615 return block.addInst(.{
616 .tag = .br,
617 .data = .{ .br = .{
618 .block_inst = target_block,
619 .operand = operand,
620 } },
621 });
622 }
623
624 fn addBinOp(
625 block: *Block,
626 tag: Air.Inst.Tag,
627 lhs: Air.Inst.Ref,
628 rhs: Air.Inst.Ref,
629 ) error{OutOfMemory}!Air.Inst.Ref {
630 return block.addInst(.{
631 .tag = tag,
632 .data = .{ .bin_op = .{
633 .lhs = lhs,
634 .rhs = rhs,
635 } },
636 });
637 }
638
639 fn addStructFieldPtr(
640 block: *Block,
641 struct_ptr: Air.Inst.Ref,
642 field_index: u32,
643 ptr_field_ty: Type,
644 ) !Air.Inst.Ref {
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 = ptr_field_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 = ptr_field_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 = .agg_field_val,
680 .data = .{ .ty_pl = .{
681 .ty = field_ty,
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 = elem_ptr_ty,
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 return block.addInst(.{
715 .tag = .ptr_elem_ptr,
716 .data = .{ .ty_pl = .{
717 .ty = elem_ptr_ty,
718 .payload = try block.sema.addExtra(Air.Bin{
719 .lhs = array_ptr,
720 .rhs = elem_index,
721 }),
722 } },
723 });
724 }
725
726 fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref {
727 const sema = block.sema;
728 const pt = sema.pt;
729 const zcu = pt.zcu;
730 return block.addInst(.{
731 .tag = if (block.float_mode == .optimized) .cmp_vector_optimized else .cmp_vector,
732 .data = .{ .ty_pl = .{
733 .ty = (try pt.vectorType(.{
734 .len = sema.typeOf(lhs).vectorLen(zcu),
735 .child = .bool_type,
736 })),
737 .payload = try sema.addExtra(Air.VectorCmp{
738 .lhs = lhs,
739 .rhs = rhs,
740 .op = Air.VectorCmp.encodeOp(cmp_op),
741 }),
742 } },
743 });
744 }
745
746 fn addReduce(block: *Block, operand: Air.Inst.Ref, operation: std.lang.ReduceOp) !Air.Inst.Ref {
747 const sema = block.sema;
748 const zcu = sema.pt.zcu;
749 const allow_optimized = switch (sema.typeOf(operand).childType(zcu).zigTypeTag(zcu)) {
750 .float => true,
751 .bool, .int => false,
752 else => unreachable,
753 };
754 return block.addInst(.{
755 .tag = if (allow_optimized and block.float_mode == .optimized) .reduce_optimized else .reduce,
756 .data = .{ .reduce = .{
757 .operand = operand,
758 .operation = operation,
759 } },
760 });
761 }
762
763 fn addAggregateInit(
764 block: *Block,
765 aggregate_ty: Type,
766 elements: []const Air.Inst.Ref,
767 ) !Air.Inst.Ref {
768 const sema = block.sema;
769 try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len);
770 const extra_index: u32 = @intCast(sema.air_extra.items.len);
771 sema.appendRefsAssumeCapacity(elements);
772
773 return block.addInst(.{
774 .tag = .aggregate_init,
775 .data = .{ .ty_pl = .{
776 .ty = aggregate_ty,
777 .payload = extra_index,
778 } },
779 });
780 }
781
782 fn addUnionInit(
783 block: *Block,
784 union_ty: Type,
785 field_index: u32,
786 init: Air.Inst.Ref,
787 ) !Air.Inst.Ref {
788 return block.addInst(.{
789 .tag = .union_init,
790 .data = .{ .ty_pl = .{
791 .ty = union_ty,
792 .payload = try block.sema.addExtra(Air.UnionInit{
793 .field_index = field_index,
794 .init = init,
795 }),
796 } },
797 });
798 }
799
800 pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
801 return (try block.addInstAsIndex(inst)).toRef();
802 }
803
804 pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
805 const sema = block.sema;
806 const gpa = sema.gpa;
807
808 try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
809 try block.instructions.ensureUnusedCapacity(gpa, 1);
810
811 const result_index: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
812 sema.air_instructions.appendAssumeCapacity(inst);
813 block.instructions.appendAssumeCapacity(result_index);
814 return result_index;
815 }
816
817 /// Insert an instruction into the block at `index`. Moves all following
818 /// instructions forward in the block to make room. Operation is O(N).
819 pub fn insertInst(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref {
820 return (try block.insertInstAsIndex(index, inst)).toRef();
821 }
822
823 pub fn insertInstAsIndex(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index {
824 const sema = block.sema;
825 const gpa = sema.gpa;
826
827 try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
828
829 const result_index: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
830 sema.air_instructions.appendAssumeCapacity(inst);
831
832 try block.instructions.insert(gpa, @backingInt(index), result_index);
833 return result_index;
834 }
835
836 pub fn ownerModule(block: Block) *Module {
837 const zcu = block.sema.pt.zcu;
838 return zcu.namespacePtr(block.namespace).fileScope(zcu).mod.?;
839 }
840
841 fn trackZir(block: *Block, inst: Zir.Inst.Index) Allocator.Error!InternPool.TrackedInst.Index {
842 const pt = block.sema.pt;
843 const comp = pt.zcu.comp;
844 block.sema.code.assertTrackable(inst);
845 return pt.zcu.intern_pool.trackZir(comp.gpa, comp.io, pt.tid, .{
846 .file = block.getFileScopeIndex(pt.zcu),
847 .inst = inst,
848 });
849 }
850
851 /// Returns the `*Block` that should be passed to `Sema.failWithOwnedErrorMsg`, because all inline
852 /// calls below it have already been reported with "called at comptime from here" notes.
853 fn explainWhyBlockIsComptime(start_block: *Block, err_msg: *Zcu.ErrorMsg) !*Block {
854 const sema = start_block.sema;
855 var block = start_block;
856 while (true) {
857 switch (block.comptime_reason.?) {
858 .inlining_parent => {
859 const inlining = block.inlining.?;
860 try sema.errNote(inlining.call_src, err_msg, "called at comptime from here", .{});
861 block = inlining.call_block;
862 },
863 .reason => |r| {
864 try r.r.explain(sema, r.src, err_msg);
865 return block;
866 },
867 }
868 }
869 }
870};
871
872/// Represents the reason we are resolving a value or evaluating code at comptime.
873/// Most reasons are represented by a `std.zig.SimpleComptimeReason`, which provides a plain message.
874const ComptimeReason = union(enum) {
875 /// Evaluating at comptime for a reason in the `std.zig.SimpleComptimeReason` enum.
876 simple: std.zig.SimpleComptimeReason,
877
878 /// Evaluating at comptime because of a comptime-only type. This field is separate so that
879 /// the type in question can be included in the error message. AstGen could never emit this
880 /// reason, because it knows nothing of types.
881 /// The format string looks like "foo '{f}' bar", where "{f}" is the comptime-only type.
882 /// We will then explain why this type is comptime-only.
883 comptime_only: struct {
884 ty: Type,
885 msg: enum {
886 union_init,
887 struct_init,
888 tuple_init,
889 },
890 },
891
892 /// Like `comptime_only`, but for a parameter type.
893 /// Includes a "parameter type declared here" note.
894 comptime_only_param_ty: struct {
895 ty: Type,
896 param_ty_src: LazySrcLoc,
897 },
898
899 /// Like `comptime_only`, but for a return type.
900 /// Includes a "return type declared here" note.
901 comptime_only_ret_ty: struct {
902 ty: Type,
903 is_generic_inst: bool,
904 ret_ty_src: LazySrcLoc,
905 },
906
907 /// Evaluating at comptime because we're evaluating an argument to a parameter marked `comptime`.
908 comptime_param: struct {
909 comptime_src: LazySrcLoc,
910 },
911
912 fn explain(reason: ComptimeReason, sema: *Sema, src: LazySrcLoc, err_msg: *Zcu.ErrorMsg) !void {
913 switch (reason) {
914 .simple => |simple| {
915 try sema.errNote(src, err_msg, "{s}", .{simple.message()});
916 },
917 .comptime_only => |co| {
918 const pre, const post = switch (co.msg) {
919 .union_init => .{ "initializer of comptime-only union", "must be comptime-known" },
920 .struct_init => .{ "initializer of comptime-only struct", "must be comptime-known" },
921 .tuple_init => .{ "initializer of comptime-only tuple", "must be comptime-known" },
922 };
923 try sema.errNote(src, err_msg, "{s} '{f}' {s}", .{ pre, co.ty.fmt(sema.pt), post });
924 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty);
925 },
926 .comptime_only_param_ty => |co| {
927 try sema.errNote(src, err_msg, "argument to parameter with comptime-only type '{f}' must be comptime-known", .{co.ty.fmt(sema.pt)});
928 try sema.errNote(co.param_ty_src, err_msg, "parameter type declared here", .{});
929 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty);
930 },
931 .comptime_only_ret_ty => |co| {
932 const function_with: []const u8 = if (co.is_generic_inst) "generic function instantiated with" else "function with";
933 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) });
934 try sema.errNote(co.ret_ty_src, err_msg, "return type declared here", .{});
935 try sema.explainWhyTypeIsComptime(err_msg, src, co.ty);
936 },
937 .comptime_param => |cp| {
938 try sema.errNote(src, err_msg, "argument to comptime parameter must be comptime-known", .{});
939 try sema.errNote(cp.comptime_src, err_msg, "parameter declared comptime here", .{});
940 },
941 }
942 }
943};
944
945/// Represents the reason a `Block` is being evaluated at comptime.
946const BlockComptimeReason = union(enum) {
947 /// This block inherits being comptime-only from the `inlining` call site.
948 inlining_parent,
949
950 /// Comptime evaluation began somewhere in the current function for a given `ComptimeReason`.
951 reason: struct {
952 /// The source location which this reason originates from. `r` is reported here.
953 src: LazySrcLoc,
954 r: ComptimeReason,
955 },
956};
957
958const LabeledBlock = struct {
959 block: Block,
960 label: Block.Label,
961
962 fn destroy(lb: *LabeledBlock, gpa: Allocator) void {
963 lb.block.instructions.deinit(gpa);
964 lb.label.merges.deinit(gpa);
965 gpa.destroy(lb);
966 }
967};
968
969/// The value stored in the inferred allocation. This will go into
970/// peer type resolution. This is stored in a separate list so that
971/// the items are contiguous in memory and thus can be passed to
972/// `Zcu.resolvePeerTypes`.
973const InferredAlloc = struct {
974 /// The placeholder `store` instructions used before the result pointer type
975 /// is known. These should be rewritten to perform any required coercions
976 /// when the type is resolved.
977 /// Allocated from `sema.arena`.
978 prongs: std.ArrayList(Air.Inst.Index) = .empty,
979};
980
981pub fn deinit(sema: *Sema) void {
982 const gpa = sema.gpa;
983 sema.air_instructions.deinit(gpa);
984 sema.air_extra.deinit(gpa);
985 sema.inst_map.deinit(gpa);
986 {
987 var it = sema.post_hoc_blocks.iterator();
988 while (it.next()) |entry| {
989 const labeled_block = entry.value_ptr.*;
990 labeled_block.destroy(gpa);
991 }
992 sema.post_hoc_blocks.deinit(gpa);
993 }
994 sema.unresolved_inferred_allocs.deinit(gpa);
995 sema.base_allocs.deinit(gpa);
996 sema.maybe_comptime_allocs.deinit(gpa);
997 sema.comptime_allocs.deinit(gpa);
998 sema.exports.deinit(gpa);
999 sema.references.deinit(gpa);
1000 sema.type_references.deinit(gpa);
1001 sema.dependencies.deinit(gpa);
1002 sema.* = undefined;
1003}
1004
1005/// Performs semantic analysis of a ZIR body which is behind a runtime condition. If comptime
1006/// control flow happens here, Sema will convert it to runtime control flow by introducing post-hoc
1007/// blocks where necessary.
1008/// Returns the branch hint for this branch.
1009fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !std.lang.BranchHint {
1010 const parent_hint = sema.branch_hint;
1011 defer sema.branch_hint = parent_hint;
1012 sema.branch_hint = null;
1013
1014 sema.analyzeBodyInner(block, body) catch |err| switch (err) {
1015 error.ComptimeBreak => {
1016 const zir_datas = sema.code.instructions.items(.data);
1017 const break_data = zir_datas[@backingInt(sema.comptime_break_inst)].@"break";
1018 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
1019 try sema.addRuntimeBreak(block, extra.block_inst, break_data.operand);
1020 },
1021 else => |e| return e,
1022 };
1023
1024 return sema.branch_hint orelse .none;
1025}
1026
1027/// Semantically analyze a ZIR function body. It is guaranteed by AstGen that such a body cannot
1028/// trigger comptime control flow to move above the function body.
1029pub fn analyzeFnBody(
1030 sema: *Sema,
1031 block: *Block,
1032 body: []const Zir.Inst.Index,
1033) !void {
1034 sema.analyzeBodyInner(block, body) catch |err| switch (err) {
1035 error.ComptimeBreak => unreachable, // unexpected comptime control flow
1036 else => |e| return e,
1037 };
1038}
1039
1040/// Given a ZIR body which can be exited via a `break_inline` instruction, or a non-inline body which
1041/// we are evaluating at comptime, semantically analyze the body and return the result from it.
1042/// Returns `null` if control flow did not break from this block, but instead terminated with some
1043/// other runtime noreturn instruction. Compile-time breaks to blocks further up the stack still
1044/// return `error.ComptimeBreak`. If `block.isComptime()`, this function will never return `null`.
1045fn analyzeInlineBody(
1046 sema: *Sema,
1047 block: *Block,
1048 body: []const Zir.Inst.Index,
1049 /// The index which a break instruction can target to break from this body.
1050 break_target: Zir.Inst.Index,
1051) CompileError!?Air.Inst.Ref {
1052 if (sema.analyzeBodyInner(block, body)) {
1053 return null;
1054 } else |err| switch (err) {
1055 error.ComptimeBreak => {},
1056 else => |e| return e,
1057 }
1058 const break_inst = sema.code.instructions.get(@backingInt(sema.comptime_break_inst));
1059 switch (break_inst.tag) {
1060 .switch_continue => {
1061 // This is handled by separate logic.
1062 return error.ComptimeBreak;
1063 },
1064 .break_inline, .@"break" => {},
1065 else => unreachable,
1066 }
1067 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data;
1068 if (extra.block_inst != break_target) {
1069 // This control flow goes further up the stack.
1070 return error.ComptimeBreak;
1071 }
1072 return sema.resolveInst(break_inst.data.@"break".operand);
1073}
1074
1075/// Like `analyzeInlineBody`, but if the body does not break with a value, returns
1076/// `.unreachable_value` instead of `null`. Notably, use this to evaluate an arbitrary
1077/// body at comptime to a single result value.
1078pub fn resolveInlineBody(
1079 sema: *Sema,
1080 block: *Block,
1081 body: []const Zir.Inst.Index,
1082 /// The index which a break instruction can target to break from this body.
1083 break_target: Zir.Inst.Index,
1084) CompileError!Air.Inst.Ref {
1085 return (try sema.analyzeInlineBody(block, body, break_target)) orelse .unreachable_value;
1086}
1087
1088/// This function is the main loop of `Sema`. It analyzes a single body of ZIR instructions.
1089///
1090/// If this function returns normally, the merges of `block` were populated with all possible
1091/// (runtime) results of this block. Peer type resolution should be performed on the result,
1092/// and relevant runtime instructions written to perform necessary coercions and breaks. See
1093/// `resolveAnalyzedBlock`. This form of return is impossible if `block.isComptime()`.
1094///
1095/// Alternatively, this function may return `error.ComptimeBreak`. This indicates that comptime
1096/// control flow is happening, and we are breaking at comptime from a block indicated by the
1097/// break instruction in `sema.comptime_break_inst`. This occurs for any `break_inline`, or for a
1098/// standard `break` at comptime. This error is pushed up the stack until the target block is
1099/// reached, at which point the break operand will be fetched.
1100///
1101/// It is rare to call this function directly. Usually, you want one of the following wrappers:
1102/// * If the body is exited via a `break_inline`, or is being evaluated at comptime,
1103/// use `Sema.analyzeInlineBody` or `Sema.resolveInlineBody`.
1104/// * If the body is behind a fresh runtime condition, use `Sema.analyzeBodyRuntimeBreak`.
1105/// * If the body is an entire function body, use `Sema.analyzeFnBody`.
1106/// * If the body is to be generated into an AIR `block`, use `Sema.resolveBlockBody`.
1107/// * Otherwise, direct usage of `Sema.analyzeBodyInner` may be necessary.
1108fn analyzeBodyInner(
1109 sema: *Sema,
1110 block: *Block,
1111 body: []const Zir.Inst.Index,
1112) CompileError!void {
1113 try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body);
1114
1115 const pt = sema.pt;
1116 const zcu = pt.zcu;
1117 const map = &sema.inst_map;
1118 const tags = sema.code.instructions.items(.tag);
1119 const datas = sema.code.instructions.items(.data);
1120
1121 var crash_info: crash_report.AnalyzeBody = undefined;
1122 crash_info.push(sema, block, body);
1123 defer crash_info.pop();
1124
1125 // We use a while (true) loop here to avoid a redundant way of breaking out of
1126 // the loop. The only way to break out of the loop is with a `noreturn`
1127 // instruction.
1128 var i: u32 = 0;
1129 while (true) {
1130 crash_info.setBodyIndex(i);
1131 const inst = body[i];
1132
1133 // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away.
1134 if (build_options.enable_logging) {
1135 std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path: {
1136 const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool);
1137 const file = zcu.fileByIndex(file_index);
1138 break :path file.path.fmt(zcu.comp);
1139 }, inst });
1140 }
1141
1142 const air_ref: Air.Inst.Ref = inst: switch (tags[@backingInt(inst)]) {
1143 // zig fmt: off
1144 .alloc => try sema.zirAlloc(block, inst),
1145 .alloc_inferred => try sema.zirAllocInferred(block, true),
1146 .alloc_inferred_mut => try sema.zirAllocInferred(block, false),
1147 .alloc_inferred_comptime => try sema.zirAllocInferredComptime(true),
1148 .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(false),
1149 .resolve_inferred_alloc => try sema.zirResolveInferredAlloc(block, inst),
1150 .alloc_mut => try sema.zirAllocMut(block, inst),
1151 .alloc_comptime_mut => try sema.zirAllocComptime(block, inst),
1152 .make_ptr_const => try sema.zirMakePtrConst(block, inst),
1153 .anyframe_type => try sema.zirAnyframeType(block, inst),
1154 .array_cat => try sema.zirArrayCat(block, inst),
1155 .array_type => try sema.zirArrayType(block, inst),
1156 .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst),
1157 .reify_int => try sema.zirReifyInt(block, inst),
1158 .vector_type => try sema.zirVectorType(block, inst),
1159 .as_node => try sema.zirAsNode(block, inst),
1160 .as_shift_operand => try sema.zirAsShiftOperand(block, inst),
1161 .bit_and => try sema.zirBitwise(block, inst, .bit_and),
1162 .bit_not => try sema.zirBitNot(block, inst),
1163 .bit_or => try sema.zirBitwise(block, inst, .bit_or),
1164 .bitcast => try sema.zirBitcast(block, inst),
1165 .suspend_block => try sema.zirSuspendBlock(block, inst),
1166 .bool_not => try sema.zirBoolNot(block, inst),
1167 .bool_br_and => try sema.zirBoolBr(block, inst, false),
1168 .bool_br_or => try sema.zirBoolBr(block, inst, true),
1169 .call => try sema.zirCall(block, inst, .direct),
1170 .field_call => try sema.zirCall(block, inst, .field),
1171 .cmp_lt => try sema.zirCmp(block, inst, .lt),
1172 .cmp_lte => try sema.zirCmp(block, inst, .lte),
1173 .cmp_eq => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .optimized)),
1174 .cmp_gte => try sema.zirCmp(block, inst, .gte),
1175 .cmp_gt => try sema.zirCmp(block, inst, .gt),
1176 .cmp_neq => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .optimized)),
1177 .decl_ref => try sema.zirDeclRef(block, inst),
1178 .decl_val => try sema.zirDeclVal(block, inst),
1179 .load => try sema.zirLoad(block, inst),
1180 .elem_ptr => try sema.zirElemPtr(block, inst),
1181 .elem_ptr_node => try sema.zirElemPtrNode(block, inst),
1182 .elem_val => try sema.zirElemVal(block, inst),
1183 .elem_ptr_load => try sema.zirElemPtrLoad(block, inst),
1184 .elem_val_imm => try sema.zirElemValImm(block, inst),
1185 .elem_type => try sema.zirElemType(block, inst),
1186 .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst),
1187 .splat_op_result_ty => try sema.zirSplatOpResultType(block, inst),
1188 .from_backing_int_arg_ty => try sema.zirFromBackingIntArgTy(block, inst),
1189 .enum_literal => try sema.zirEnumLiteral(block, inst),
1190 .decl_literal => try sema.zirDeclLiteral(block, inst, true),
1191 .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false),
1192 .int_from_enum => try sema.zirIntFromEnum(block, inst),
1193 .enum_from_int => try sema.zirEnumFromInt(block, inst),
1194 .backing_int => try sema.zirBackingInt(block, inst),
1195 .from_backing_int => try sema.zirFromBackingInt(block, inst),
1196 .err_union_code => try sema.zirErrUnionCode(block, inst),
1197 .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst),
1198 .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst),
1199 .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst),
1200 .error_union_type => try sema.zirErrorUnionType(block, inst),
1201 .error_value => try sema.zirErrorValue(block, inst),
1202 .field_ptr => try sema.zirFieldPtr(block, inst),
1203 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
1204 .field_ptr_load => try sema.zirFieldPtrLoad(block, inst),
1205 .field_ptr_named_load => try sema.zirFieldPtrNamedLoad(block, inst),
1206 .func => try sema.zirFunc(block, inst, false),
1207 .func_inferred => try sema.zirFunc(block, inst, true),
1208 .func_fancy => try sema.zirFuncFancy(block, inst),
1209 .import => try sema.zirImport(block, inst),
1210 .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst),
1211 .int => try sema.zirInt(block, inst),
1212 .int_big => try sema.zirIntBig(block, inst),
1213 .float => try sema.zirFloat(block, inst),
1214 .float128 => try sema.zirFloat128(block, inst),
1215 .int_type => try sema.zirIntType(inst),
1216 .is_non_err => try sema.zirIsNonErr(block, inst),
1217 .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst),
1218 .ret_is_non_err => try sema.zirRetIsNonErr(block, inst),
1219 .is_non_null => try sema.zirIsNonNull(block, inst),
1220 .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst),
1221 .merge_error_sets => try sema.zirMergeErrorSets(block, inst),
1222 .negate => try sema.zirNegate(block, inst),
1223 .negate_wrap => try sema.zirNegateWrap(block, inst),
1224 .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true),
1225 .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true),
1226 .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false),
1227 .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false),
1228 .optional_type => try sema.zirOptionalType(block, inst),
1229 .ptr_type => try sema.zirPtrType(block, inst),
1230 .ref => try sema.zirRef(block, inst),
1231 .deref => try sema.zirDeref(block, inst),
1232 .ref_deref => try sema.zirRefDeref(block, inst),
1233 .shr => try sema.zirShr(block, inst, .shr),
1234 .shr_exact => try sema.zirShr(block, inst, .shr_exact),
1235 .slice_end => try sema.zirSliceEnd(block, inst),
1236 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
1237 .slice_start => try sema.zirSliceStart(block, inst),
1238 .slice_length => try sema.zirSliceLength(block, inst),
1239 .slice_sentinel_ty => try sema.zirSliceSentinelTy(block, inst),
1240 .str => try sema.zirStr(inst),
1241 .switch_block => try sema.zirSwitchBlock(block, inst, false),
1242 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true),
1243 .switch_block_err_union => try sema.zirSwitchBlockErrUnion(block, inst),
1244 .type_info => try sema.zirTypeInfo(block, inst),
1245 .size_of => try sema.zirSizeOf(block, inst),
1246 .bit_size_of => try sema.zirBitSizeOf(block, inst),
1247 .typeof => try sema.zirTypeof(block, inst),
1248 .typeof_builtin => try sema.zirTypeofBuiltin(block, inst),
1249 .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst),
1250 .xor => try sema.zirBitwise(block, inst, .xor),
1251 .struct_init_empty => try sema.zirStructInitEmpty(block, inst),
1252 .struct_init_empty_result => try sema.zirStructInitEmptyResult(block, inst, false),
1253 .struct_init_empty_ref_result => try sema.zirStructInitEmptyResult(block, inst, true),
1254 .struct_init_anon => try sema.zirStructInitAnon(block, inst),
1255 .struct_init => try sema.zirStructInit(block, inst, false),
1256 .struct_init_ref => try sema.zirStructInit(block, inst, true),
1257 .struct_init_field_type => try sema.zirStructInitFieldType(block, inst),
1258 .struct_init_field_ptr => try sema.zirStructInitFieldPtr(block, inst),
1259 .array_init_anon => try sema.zirArrayInitAnon(block, inst),
1260 .array_init => try sema.zirArrayInit(block, inst, false),
1261 .array_init_ref => try sema.zirArrayInit(block, inst, true),
1262 .array_init_elem_type => try sema.zirArrayInitElemType(block, inst),
1263 .array_init_elem_ptr => try sema.zirArrayInitElemPtr(block, inst),
1264 .union_init => try sema.zirUnionInit(block, inst),
1265 .field_type_ref => try sema.zirFieldTypeRef(block, inst),
1266 .int_from_ptr => try sema.zirIntFromPtr(block, inst),
1267 .align_of => try sema.zirAlignOf(block, inst),
1268 .int_from_bool => try sema.zirIntFromBool(block, inst),
1269 .embed_file => try sema.zirEmbedFile(block, inst),
1270 .error_name => try sema.zirErrorName(block, inst),
1271 .tag_name => try sema.zirTagName(block, inst),
1272 .type_name => try sema.zirTypeName(block, inst),
1273 .frame_type => try sema.zirFrameType(block, inst),
1274 .int_from_float => try sema.zirIntFromFloat(block, inst),
1275 .float_from_int => try sema.zirFloatFromInt(block, inst),
1276 .ptr_from_int => try sema.zirPtrFromInt(block, inst),
1277 .float_cast => try sema.zirFloatCast(block, inst),
1278 .int_cast => try sema.zirIntCast(block, inst),
1279 .ptr_cast => try sema.zirPtrCast(block, inst),
1280 .truncate => try sema.zirTruncate(block, inst),
1281 .has_decl => try sema.zirHasDecl(block, inst),
1282 .has_field => try sema.zirHasField(block, inst),
1283 .byte_swap => try sema.zirByteSwap(block, inst),
1284 .bit_reverse => try sema.zirBitReverse(block, inst),
1285 .bit_offset_of => try sema.zirBitOffsetOf(block, inst),
1286 .offset_of => try sema.zirOffsetOf(block, inst),
1287 .splat => try sema.zirSplat(block, inst),
1288 .reduce => try sema.zirReduce(block, inst),
1289 .shuffle => try sema.zirShuffle(block, inst),
1290 .atomic_load => try sema.zirAtomicLoad(block, inst),
1291 .atomic_rmw => try sema.zirAtomicRmw(block, inst),
1292 .mul_add => try sema.zirMulAdd(block, inst),
1293 .builtin_call => try sema.zirBuiltinCall(block, inst),
1294 .@"resume" => try sema.zirResume(block, inst),
1295 .for_len => try sema.zirForLen(block, inst),
1296 .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst),
1297 .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst),
1298 .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst),
1299
1300 .clz => try sema.zirBitCount(block, inst, .clz, Value.clz),
1301 .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz),
1302 .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount),
1303 .abs => try sema.zirAbs(block, inst),
1304
1305 .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt),
1306 .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin),
1307 .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos),
1308 .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan),
1309 .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp),
1310 .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2),
1311 .log => try sema.zirUnaryMath(block, inst, .log, Value.log),
1312 .log2 => try sema.zirUnaryMath(block, inst, .log2, Value.log2),
1313 .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10),
1314 .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor),
1315 .ceil => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil),
1316 .round => try sema.zirUnaryMath(block, inst, .round, Value.round),
1317 .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc),
1318
1319 .error_set_decl => try sema.zirErrorSetDecl(inst),
1320
1321 .add => try sema.zirArithmetic(block, inst, .add, true),
1322 .addwrap => try sema.zirArithmetic(block, inst, .addwrap, true),
1323 .add_sat => try sema.zirArithmetic(block, inst, .add_sat, true),
1324 .add_unsafe => try sema.zirArithmetic(block, inst, .add_unsafe, false),
1325 .mul => try sema.zirArithmetic(block, inst, .mul, true),
1326 .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap, true),
1327 .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat, true),
1328 .sub => try sema.zirArithmetic(block, inst, .sub, true),
1329 .subwrap => try sema.zirArithmetic(block, inst, .subwrap, true),
1330 .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat, true),
1331
1332 .div => try sema.zirDiv(block, inst),
1333 .div_exact => try sema.zirDivExact(block, inst),
1334 .div_floor => try sema.zirDivFloor(block, inst),
1335 .div_ceil => try sema.zirDivCeil(block, inst),
1336 .div_trunc => try sema.zirDivTrunc(block, inst),
1337
1338 .mod_rem => try sema.zirModRem(block, inst),
1339 .mod => try sema.zirMod(block, inst),
1340 .rem => try sema.zirRem(block, inst),
1341
1342 .max => try sema.zirMinMax(block, inst, .max),
1343 .min => try sema.zirMinMax(block, inst, .min),
1344
1345 .shl => try sema.zirShl(block, inst, .shl),
1346 .shl_exact => try sema.zirShl(block, inst, .shl_exact),
1347 .shl_sat => try sema.zirShl(block, inst, .shl_sat),
1348
1349 .ret_ptr => try sema.zirRetPtr(block, inst),
1350 .ret_type => Air.internedToRef(sema.fn_ret_ty.toIntern()),
1351
1352 // Instructions that we know to *always* be noreturn based solely on their tag.
1353 // These functions match the return type of analyzeBody so that we can
1354 // tail call them here.
1355 .compile_error => break try sema.zirCompileError(block, inst),
1356 .ret_implicit => break try sema.zirRetImplicit(block, inst),
1357 .ret_node => break try sema.zirRetNode(block, inst),
1358 .ret_load => break try sema.zirRetLoad(block, inst),
1359 .ret_err_value => break try sema.zirRetErrValue(block, inst),
1360 .@"unreachable" => break try sema.zirUnreachable(block, inst),
1361 .panic => break try sema.zirPanic(block, inst),
1362 .trap => break try sema.zirTrap(block, inst),
1363 // zig fmt: on
1364
1365 // This instruction never exists in an analyzed body. It exists only in the declaration
1366 // list for a container type.
1367 .declaration => unreachable,
1368
1369 .extended => ext: {
1370 const extended = datas[@backingInt(inst)].extended;
1371 break :ext switch (extended.opcode) {
1372 // zig fmt: off
1373 .struct_decl => try sema.zirStructDecl( block, inst),
1374 .enum_decl => try sema.zirEnumDecl( block, inst),
1375 .union_decl => try sema.zirUnionDecl( block, inst),
1376 .opaque_decl => try sema.zirOpaqueDecl( block, inst),
1377 .tuple_decl => try sema.zirTupleDecl( block, extended),
1378 .this => try sema.zirThis( block, extended),
1379 .ret_addr => try sema.zirRetAddr( block, extended),
1380 .builtin_src => try sema.zirBuiltinSrc( block, extended),
1381 .error_return_trace => try sema.zirErrorReturnTrace( block),
1382 .frame => try sema.zirFrame( block, extended),
1383 .frame_address => try sema.zirFrameAddress( block, extended),
1384 .alloc => try sema.zirAllocExtended( block, extended),
1385 .builtin_extern => try sema.zirBuiltinExtern( block, extended),
1386 .@"asm" => try sema.zirAsm( block, extended, false),
1387 .asm_expr => try sema.zirAsm( block, extended, true),
1388 .typeof_peer => try sema.zirTypeofPeer( block, extended, inst),
1389 .round_op => try sema.zirRoundCast( block, extended),
1390 .round_op_ty => try sema.zirRoundOpType( block, extended),
1391 .compile_log => try sema.zirCompileLog( block, extended),
1392 .min_multi => try sema.zirMinMaxMulti( block, extended, .min),
1393 .max_multi => try sema.zirMinMaxMulti( block, extended, .max),
1394 .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
1395 .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
1396 .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
1397 .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode),
1398 .wasm_memory_size => try sema.zirWasmMemorySize( block, extended),
1399 .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended),
1400 .prefetch => try sema.zirPrefetch( block, extended),
1401 .error_cast => try sema.zirErrorCast( block, extended),
1402 .select => try sema.zirSelect( block, extended),
1403 .int_from_error => try sema.zirIntFromError( block, extended),
1404 .error_from_int => try sema.zirErrorFromInt( block, extended),
1405 .cmpxchg => try sema.zirCmpxchg( block, extended),
1406 .c_va_arg => try sema.zirCVaArg( block, extended),
1407 .c_va_copy => try sema.zirCVaCopy( block, extended),
1408 .c_va_end => try sema.zirCVaEnd( block, extended),
1409 .c_va_start => try sema.zirCVaStart( block, extended),
1410 .ptr_cast_full => try sema.zirPtrCastFull( block, extended),
1411 .ptr_cast_no_dest => try sema.zirPtrCastNoDest( block, extended),
1412 .work_item_id => try sema.zirWorkItem( block, extended, extended.opcode),
1413 .work_group_size => try sema.zirWorkItem( block, extended, extended.opcode),
1414 .work_group_id => try sema.zirWorkItem( block, extended, extended.opcode),
1415 .in_comptime => try sema.zirInComptime( block),
1416 .closure_get => try sema.zirClosureGet( block, extended),
1417
1418 .reify_slice_arg_ty => try sema.zirReifySliceArgTy( block, extended),
1419 .reify_enum_value_slice_ty => try sema.zirReifyEnumValueSliceTy(block, extended),
1420 .reify_pointer_sentinel_ty => try sema.zirReifyPointerSentinelTy(block, extended),
1421 .reify_tuple => try sema.zirReifyTuple( block, extended),
1422 .reify_pointer => try sema.zirReifyPointer( block, extended),
1423 .reify_fn => try sema.zirReifyFn( block, extended),
1424 .reify_struct => try sema.zirReifyStruct( block, extended, inst),
1425 .reify_union => try sema.zirReifyUnion( block, extended, inst),
1426 .reify_enum => try sema.zirReifyEnum( block, extended, inst),
1427 .reify_spirv_type => try sema.zirReifySpirvType( block, extended, inst),
1428 // zig fmt: on
1429
1430 .set_float_mode => {
1431 try sema.zirSetFloatMode(block, extended);
1432 i += 1;
1433 continue;
1434 },
1435 .breakpoint => {
1436 try sema.zirBreakpoint(block, extended);
1437 i += 1;
1438 continue;
1439 },
1440 .disable_instrumentation => {
1441 try sema.zirDisableInstrumentation();
1442 i += 1;
1443 continue;
1444 },
1445 .disable_intrinsics => {
1446 try sema.zirDisableIntrinsics();
1447 i += 1;
1448 continue;
1449 },
1450 .restore_err_ret_index => {
1451 try sema.zirRestoreErrRetIndex(block, extended);
1452 i += 1;
1453 continue;
1454 },
1455 .branch_hint => {
1456 try sema.zirBranchHint(block, extended);
1457 i += 1;
1458 continue;
1459 },
1460 .value_placeholder => unreachable, // never appears in a body
1461 .field_parent_ptr => try sema.zirFieldParentPtr(block, extended),
1462 .std_lang_value => try sema.zirStdLangValue(block, extended),
1463 .inplace_arith_result_ty => try sema.zirInplaceArithResultTy(extended),
1464 .dbg_empty_stmt => {
1465 try sema.zirDbgEmptyStmt(block, inst);
1466 i += 1;
1467 continue;
1468 },
1469 .astgen_error => return sema.failTransitive(.astgen_error),
1470 .float_op_result_ty => try sema.zirFloatOpResultType(block, extended),
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_destructure => {
1560 try sema.zirValidateDestructure(block, inst);
1561 i += 1;
1562 continue;
1563 },
1564 .validate_ref_ty => {
1565 try sema.zirValidateRefTy(block, inst);
1566 i += 1;
1567 continue;
1568 },
1569 .validate_const => {
1570 try sema.zirValidateConst(block, inst);
1571 i += 1;
1572 continue;
1573 },
1574 .@"export" => {
1575 try sema.zirExport(block, inst);
1576 i += 1;
1577 continue;
1578 },
1579 .set_runtime_safety => {
1580 try sema.zirSetRuntimeSafety(block, inst);
1581 i += 1;
1582 continue;
1583 },
1584 .param => {
1585 try sema.zirParam(block, inst, false);
1586 i += 1;
1587 continue;
1588 },
1589 .param_comptime => {
1590 try sema.zirParam(block, inst, true);
1591 i += 1;
1592 continue;
1593 },
1594 .param_anytype => {
1595 try sema.zirParamAnytype(block, inst, false);
1596 i += 1;
1597 continue;
1598 },
1599 .param_anytype_comptime => {
1600 try sema.zirParamAnytype(block, inst, true);
1601 i += 1;
1602 continue;
1603 },
1604 .memcpy => {
1605 try sema.zirMemcpy(block, inst, .memcpy, true);
1606 i += 1;
1607 continue;
1608 },
1609 .memmove => {
1610 try sema.zirMemcpy(block, inst, .memmove, false);
1611 i += 1;
1612 continue;
1613 },
1614 .memset => {
1615 try sema.zirMemset(block, inst);
1616 i += 1;
1617 continue;
1618 },
1619 .check_comptime_control_flow => {
1620 if (!block.isComptime()) {
1621 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
1622 const src = block.nodeOffset(inst_data.src_node);
1623 const inline_block = inst_data.operand.toIndex().?;
1624
1625 var check_block = block;
1626 const target_runtime_index = while (true) {
1627 if (check_block.inline_block == inline_block.toOptional()) {
1628 break check_block.runtime_index;
1629 }
1630 check_block = check_block.parent.?;
1631 };
1632
1633 if (@backingInt(target_runtime_index) < @backingInt(block.runtime_index)) {
1634 const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
1635 const msg = msg: {
1636 const msg = try sema.errMsg(src, "comptime control flow inside runtime block", .{});
1637 errdefer msg.destroy(sema.gpa);
1638 try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
1639 break :msg msg;
1640 };
1641 return sema.failWithOwnedErrorMsg(block, msg);
1642 }
1643 }
1644 i += 1;
1645 continue;
1646 },
1647 .save_err_ret_index => {
1648 try sema.zirSaveErrRetIndex(block, inst);
1649 i += 1;
1650 continue;
1651 },
1652 .restore_err_ret_index_unconditional => {
1653 const un_node = datas[@backingInt(inst)].un_node;
1654 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), un_node.operand, .none);
1655 i += 1;
1656 continue;
1657 },
1658 .restore_err_ret_index_fn_entry => {
1659 const un_node = datas[@backingInt(inst)].un_node;
1660 try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), .none, un_node.operand);
1661 i += 1;
1662 continue;
1663 },
1664
1665 // Special case instructions to handle comptime control flow.
1666 .@"break" => {
1667 if (block.isComptime()) {
1668 sema.comptime_break_inst = inst;
1669 return error.ComptimeBreak;
1670 } else {
1671 try sema.zirBreak(block, inst);
1672 break;
1673 }
1674 },
1675 .break_inline => {
1676 sema.comptime_break_inst = inst;
1677 return error.ComptimeBreak;
1678 },
1679 .repeat => {
1680 if (block.isComptime()) {
1681 // Send comptime control flow back to the beginning of this block.
1682 const src = block.nodeOffset(datas[@backingInt(inst)].node);
1683 try sema.emitBackwardBranch(block, src);
1684 i = 0;
1685 continue;
1686 } else {
1687 // We are definitely called by `zirLoop`, which will treat the
1688 // fact that this body does not terminate `noreturn` as an
1689 // implicit repeat.
1690 // TODO: since AIR has `repeat` now, we could change ZIR to generate
1691 // more optimal code utilizing `repeat` instructions across blocks!
1692 break;
1693 }
1694 },
1695 .repeat_inline => {
1696 // Send comptime control flow back to the beginning of this block.
1697 const src = block.nodeOffset(datas[@backingInt(inst)].node);
1698 try sema.emitBackwardBranch(block, src);
1699 i = 0;
1700 continue;
1701 },
1702 .switch_continue => if (block.isComptime()) {
1703 sema.comptime_break_inst = inst;
1704 return error.ComptimeBreak;
1705 } else {
1706 try sema.zirSwitchContinue(block, inst);
1707 break;
1708 },
1709
1710 .loop => if (block.isComptime()) {
1711 continue :inst .block_inline;
1712 } else try sema.zirLoop(block, inst),
1713
1714 .block => if (block.isComptime()) {
1715 continue :inst .block_inline;
1716 } else try sema.zirBlock(block, inst),
1717
1718 .block_comptime => {
1719 const pl_node = datas[@backingInt(inst)].pl_node;
1720 const src = block.nodeOffset(pl_node.src_node);
1721 const extra = sema.code.extraData(Zir.Inst.BlockComptime, pl_node.payload_index);
1722 const block_body = sema.code.bodySlice(extra.end, extra.data.body_len);
1723
1724 var child_block = block.makeSubBlock();
1725 defer child_block.instructions.deinit(sema.gpa);
1726
1727 // We won't have any merges, but we must ensure this block is properly labeled for
1728 // any `.restore_err_ret_index_*` instructions.
1729 var label: Block.Label = .{
1730 .zir_block = inst,
1731 .merges = undefined,
1732 };
1733 child_block.label = &label;
1734
1735 child_block.comptime_reason = .{ .reason = .{
1736 .src = src,
1737 .r = .{ .simple = extra.data.reason },
1738 } };
1739
1740 const result = try sema.resolveInlineBody(&child_block, block_body, inst);
1741
1742 // Only check for the result being comptime-known in the outermost `block_comptime`.
1743 // That way, AstGen can safely elide redundant `block_comptime` without affecting semantics.
1744 if (!block.isComptime() and !try sema.isComptimeKnown(result)) {
1745 return sema.failWithNeededComptime(&child_block, src, null);
1746 }
1747
1748 break :inst result;
1749 },
1750
1751 .block_inline => blk: {
1752 // Directly analyze the block body without introducing a new block.
1753 // However, in the case of a corresponding break_inline which reaches
1754 // through a runtime conditional branch, we must retroactively emit
1755 // a block, so we remember the block index here just in case.
1756 const block_index = block.instructions.items.len;
1757 const inst_data = datas[@backingInt(inst)].pl_node;
1758 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
1759 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
1760 const gpa = sema.gpa;
1761
1762 const BreakResult = struct {
1763 block_inst: Zir.Inst.Index,
1764 operand: Zir.Inst.Ref,
1765 };
1766
1767 const opt_break_data: ?BreakResult, const need_debug_scope = b: {
1768 // Create a temporary child block so that this inline block is properly
1769 // labeled for any .restore_err_ret_index instructions
1770 var child_block = block.makeSubBlock();
1771 var need_debug_scope = false;
1772 child_block.need_debug_scope = &need_debug_scope;
1773
1774 // If this block contains a function prototype, we need to reset the
1775 // current list of parameters and restore it later.
1776 // Note: this probably needs to be resolved in a more general manner.
1777 const tag_index = @backingInt(inline_body[inline_body.len - 1]);
1778 child_block.inline_block = (if (tags[tag_index] == .repeat_inline)
1779 inline_body[0]
1780 else
1781 inst).toOptional();
1782
1783 var label: Block.Label = .{
1784 .zir_block = inst,
1785 .merges = undefined,
1786 };
1787 child_block.label = &label;
1788
1789 // Write these instructions directly into the parent block
1790 child_block.instructions = block.instructions;
1791 defer block.instructions = child_block.instructions;
1792
1793 const break_result: ?BreakResult = if (sema.analyzeBodyInner(&child_block, inline_body)) r: {
1794 break :r null;
1795 } else |err| switch (err) {
1796 error.ComptimeBreak => brk_res: {
1797 const break_inst = sema.comptime_break_inst;
1798 const break_data = sema.code.instructions.items(.data)[@backingInt(break_inst)].@"break";
1799 const break_extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
1800 break :brk_res .{
1801 .block_inst = break_extra.block_inst,
1802 .operand = break_data.operand,
1803 };
1804 },
1805 else => |e| return e,
1806 };
1807
1808 if (need_debug_scope) {
1809 _ = try sema.ensurePostHoc(block, inst);
1810 }
1811
1812 break :b .{ break_result, need_debug_scope };
1813 };
1814
1815 // A runtime conditional branch that needs a post-hoc block to be
1816 // emitted communicates this by mapping the block index into the inst map.
1817 if (map.get(inst)) |new_block_ref| ph: {
1818 // Comptime control flow populates the map, so we don't actually know
1819 // if this is a post-hoc runtime block until we check the
1820 // post_hoc_block map.
1821 const new_block_inst = new_block_ref.toIndex() orelse break :ph;
1822 const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse
1823 break :ph;
1824
1825 // In this case we need to move all the instructions starting at
1826 // block_index from the current block into this new one.
1827
1828 if (opt_break_data) |break_data| {
1829 // This is a comptime break which we now change to a runtime break
1830 // since it crosses a runtime branch.
1831 // It may pass through our currently being analyzed block_inline or it
1832 // may point directly to it. In the latter case, this modifies the
1833 // block that we looked up in the post_hoc_blocks map above.
1834 try sema.addRuntimeBreak(block, break_data.block_inst, break_data.operand);
1835 }
1836
1837 try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]);
1838 block.instructions.items.len = block_index;
1839
1840 const block_result = try sema.resolveAnalyzedBlock(block, block.nodeOffset(inst_data.src_node), &labeled_block.block, &labeled_block.label.merges, need_debug_scope);
1841 {
1842 // Destroy the ad-hoc block entry so that it does not interfere with
1843 // the next iteration of comptime control flow, if any.
1844 labeled_block.destroy(gpa);
1845 assert(sema.post_hoc_blocks.remove(new_block_inst));
1846 }
1847
1848 break :blk block_result;
1849 }
1850
1851 const break_data = opt_break_data orelse break;
1852 if (inst == break_data.block_inst) {
1853 break :blk sema.resolveInst(break_data.operand);
1854 } else {
1855 // `comptime_break_inst` preserved from `analyzeBodyInner` above.
1856 return error.ComptimeBreak;
1857 }
1858 },
1859 .condbr => if (block.isComptime()) {
1860 continue :inst .condbr_inline;
1861 } else {
1862 try sema.zirCondbr(block, inst);
1863 break;
1864 },
1865 .condbr_inline => {
1866 const inst_data = datas[@backingInt(inst)].pl_node;
1867 const cond_src = block.src(.{ .node_offset_if_cond = inst_data.src_node });
1868 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
1869 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
1870 const else_body = sema.code.bodySlice(
1871 extra.end + then_body.len,
1872 extra.data.else_body_len,
1873 );
1874 const uncasted_cond = sema.resolveInst(extra.data.condition);
1875 const cond = try sema.coerce(block, .bool, uncasted_cond, cond_src);
1876 const cond_val = try sema.resolveConstDefinedValue(
1877 block,
1878 cond_src,
1879 cond,
1880 // If this block is comptime, it's more helpful to just give the outer message.
1881 // This is particularly true if this came from a comptime `condbr` above.
1882 if (block.isComptime()) null else .{ .simple = .inline_loop_operand },
1883 );
1884 const inline_body = if (cond_val.toBool()) then_body else else_body;
1885
1886 try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src);
1887 const old_runtime_index = block.runtime_index;
1888 defer block.runtime_index = old_runtime_index;
1889
1890 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
1891 break :inst result;
1892 },
1893 .@"try" => blk: {
1894 if (!block.isComptime()) break :blk try sema.zirTry(block, inst);
1895 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
1896 const src = block.nodeOffset(inst_data.src_node);
1897 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node });
1898 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1899 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
1900 const err_union = sema.resolveInst(extra.data.operand);
1901 const err_union_ty = sema.typeOf(err_union);
1902 if (err_union_ty.zigTypeTag(zcu) != .error_union) {
1903 return sema.failWithOwnedErrorMsg(block, msg: {
1904 const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
1905 errdefer msg.destroy(sema.gpa);
1906 try sema.addDeclaredHereNote(msg, err_union_ty);
1907 try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
1908 break :msg msg;
1909 });
1910 }
1911 const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?;
1912 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null);
1913 if (is_non_err_val.toBool()) {
1914 break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false);
1915 }
1916 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
1917 break :blk result;
1918 },
1919 .try_ptr => blk: {
1920 if (!block.isComptime()) break :blk try sema.zirTryPtr(block, inst);
1921 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
1922 const src = block.nodeOffset(inst_data.src_node);
1923 const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node });
1924 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1925 const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
1926 const operand = sema.resolveInst(extra.data.operand);
1927 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
1928 const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?;
1929 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null);
1930 if (is_non_err_val.toBool()) {
1931 break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
1932 }
1933 const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break;
1934 break :blk result;
1935 },
1936 .@"defer" => blk: {
1937 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"defer";
1938 const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len);
1939 if (sema.analyzeBodyInner(block, defer_body)) {
1940 // The defer terminated noreturn - no more analysis needed.
1941 break;
1942 } else |err| switch (err) {
1943 error.ComptimeBreak => {},
1944 else => |e| return e,
1945 }
1946 if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) {
1947 return error.ComptimeBreak;
1948 }
1949 break :blk .void_value;
1950 },
1951 };
1952
1953 const is_inferred_alloc = if (air_ref.toIndex()) |air_inst| switch (sema.air_instructions.items(.tag)[@backingInt(air_inst)]) {
1954 .inferred_alloc, .inferred_alloc_comptime => true,
1955 else => false,
1956 } else false;
1957 // We must resolve the layout of a type before creating a value of that type. Therefore,
1958 // the layout of the type of `air_ref` must already be resolved. The call to `classify`
1959 // doubles as an assertion of this.
1960 if (!is_inferred_alloc) switch (sema.typeOf(air_ref).classify(zcu)) {
1961 .no_possible_value => {
1962 // The instruction result was noreturn, which should mean that the body itself now
1963 // ends with a noreturn instruction. Let's confirm that.
1964 const last_inst = block.instructions.items[block.instructions.items.len - 1];
1965 const last_inst_ty = sema.typeOf(last_inst.toRef());
1966 assert(last_inst_ty.classify(zcu) == .no_possible_value);
1967 break;
1968 },
1969 .one_possible_value => assert(air_ref.toInterned() != null), // the value should be comptime-known
1970 .partially_comptime => assert(air_ref.toInterned() != null), // the value should be comptime-known
1971 .fully_comptime => assert(air_ref.toInterned() != null), // the value should be comptime-known
1972 .runtime => {},
1973 };
1974
1975 map.putAssumeCapacity(inst, air_ref);
1976 i += 1;
1977 }
1978}
1979
1980fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref {
1981 if (zir_ref == .none) {
1982 return .none;
1983 } else {
1984 return resolveInst(sema, zir_ref);
1985 }
1986}
1987
1988fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref {
1989 assert(zir_ref != .none);
1990 if (zir_ref.toIndex()) |i| {
1991 return sema.inst_map.get(i).?;
1992 }
1993 // First section of indexes correspond to a set number of constant values.
1994 // We intentionally map the same indexes to the same values between ZIR and AIR.
1995 return @fromBackingInt(@intCast(@backingInt(zir_ref)));
1996}
1997
1998fn resolveConstBool(
1999 sema: *Sema,
2000 block: *Block,
2001 src: LazySrcLoc,
2002 zir_ref: Zir.Inst.Ref,
2003 reason: ComptimeReason,
2004) !bool {
2005 const air_inst = sema.resolveInst(zir_ref);
2006 const wanted_type: Type = .bool;
2007 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
2008 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
2009 return val.toBool();
2010}
2011
2012fn resolveConstString(
2013 sema: *Sema,
2014 block: *Block,
2015 src: LazySrcLoc,
2016 zir_ref: Zir.Inst.Ref,
2017 /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value
2018 /// being comptime-resolved is that the block is being comptime-evaluated.
2019 reason: ?ComptimeReason,
2020) ![]u8 {
2021 const air_inst = sema.resolveInst(zir_ref);
2022 return sema.toConstString(block, src, air_inst, reason);
2023}
2024
2025pub fn toConstString(
2026 sema: *Sema,
2027 block: *Block,
2028 src: LazySrcLoc,
2029 air_inst: Air.Inst.Ref,
2030 /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value
2031 /// being comptime-resolved is that the block is being comptime-evaluated.
2032 reason: ?ComptimeReason,
2033) ![]u8 {
2034 const pt = sema.pt;
2035 const coerced_inst = try sema.coerce(block, .slice_const_u8, air_inst, src);
2036 const slice_val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
2037 const arr_val = try sema.derefSliceAsArray(block, src, slice_val, reason);
2038 return arr_val.toAllocatedBytes(arr_val.typeOf(pt.zcu), sema.arena, pt);
2039}
2040
2041pub fn resolveConstStringIntern(
2042 sema: *Sema,
2043 block: *Block,
2044 src: LazySrcLoc,
2045 zir_ref: Zir.Inst.Ref,
2046 reason: ComptimeReason,
2047) !InternPool.NullTerminatedString {
2048 const air_inst = sema.resolveInst(zir_ref);
2049 const wanted_type: Type = .slice_const_u8;
2050 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
2051 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason);
2052 return sema.sliceToIpString(block, src, val, reason);
2053}
2054
2055fn resolveTypeOrPoison(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !?Type {
2056 const air_inst = sema.resolveInst(zir_ref);
2057 const ty = try sema.analyzeAsType(block, src, .type, air_inst);
2058 if (ty.isGenericPoison()) return null;
2059 return ty;
2060}
2061
2062pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type {
2063 return (try sema.resolveTypeOrPoison(block, src, zir_ref)).?;
2064}
2065
2066fn resolveDestType(
2067 sema: *Sema,
2068 block: *Block,
2069 src: LazySrcLoc,
2070 zir_ref: Zir.Inst.Ref,
2071 strat: enum { remove_eu_opt, remove_eu, remove_opt },
2072 builtin_name: []const u8,
2073) !Type {
2074 const pt = sema.pt;
2075 const zcu = pt.zcu;
2076 const remove_eu = switch (strat) {
2077 .remove_eu_opt, .remove_eu => true,
2078 .remove_opt => false,
2079 };
2080 const remove_opt = switch (strat) {
2081 .remove_eu_opt, .remove_opt => true,
2082 .remove_eu => false,
2083 };
2084
2085 const raw_ty = try sema.resolveTypeOrPoison(block, src, zir_ref) orelse {
2086 // Cast builtins use their result type as the destination type, but
2087 // it could be an anytype argument, which we can't catch in AstGen.
2088 const msg = msg: {
2089 const msg = try sema.errMsg(src, "{s} must have a known result type", .{builtin_name});
2090 errdefer msg.destroy(sema.gpa);
2091 switch (sema.genericPoisonReason(block, zir_ref)) {
2092 .anytype_param => |call_src| try sema.errNote(call_src, msg, "result type is unknown due to anytype parameter", .{}),
2093 .anyopaque_ptr => |ptr_src| try sema.errNote(ptr_src, msg, "result type is unknown due to opaque pointer type", .{}),
2094 .unknown => {},
2095 }
2096 try sema.errNote(src, msg, "use @as to provide explicit result type", .{});
2097 break :msg msg;
2098 };
2099 return sema.failWithOwnedErrorMsg(block, msg);
2100 };
2101
2102 if (remove_eu and raw_ty.zigTypeTag(zcu) == .error_union) {
2103 const eu_child = raw_ty.errorUnionPayload(zcu);
2104 if (remove_opt and eu_child.zigTypeTag(zcu) == .optional) {
2105 return eu_child.childType(zcu);
2106 }
2107 return eu_child;
2108 }
2109 if (remove_opt and raw_ty.zigTypeTag(zcu) == .optional) {
2110 return raw_ty.childType(zcu);
2111 }
2112 return raw_ty;
2113}
2114
2115const GenericPoisonReason = union(enum) {
2116 anytype_param: LazySrcLoc,
2117 anyopaque_ptr: LazySrcLoc,
2118 unknown,
2119};
2120
2121/// Backtracks through ZIR instructions to determine the reason a generic poison
2122/// type was created. Used for error reporting.
2123fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoisonReason {
2124 var cur = ref;
2125 while (true) {
2126 const inst = cur.toIndex() orelse return .unknown;
2127 switch (sema.code.instructions.items(.tag)[@backingInt(inst)]) {
2128 .validate_array_init_ref_ty => {
2129 const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
2130 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
2131 cur = extra.ptr_ty;
2132 },
2133 .array_init_elem_type => {
2134 const bin = sema.code.instructions.items(.data)[@backingInt(inst)].bin;
2135 cur = bin.lhs;
2136 },
2137 .indexable_ptr_elem_type, .splat_op_result_ty => {
2138 const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
2139 cur = un_node.operand;
2140 },
2141 .struct_init_field_type => {
2142 const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
2143 const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data;
2144 cur = extra.container_type;
2145 },
2146 .elem_type => {
2147 // There are two cases here: the pointer type may already have been
2148 // generic poison, or it may have been an anyopaque pointer.
2149 const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
2150 const operand_ref = sema.resolveInst(un_node.operand);
2151 const operand_val = operand_ref.toInterned() orelse return .unknown;
2152 if (operand_val == .generic_poison_type) {
2153 // The pointer was generic poison - keep looking.
2154 cur = un_node.operand;
2155 } else {
2156 // This must be an anyopaque pointer!
2157 return .{ .anyopaque_ptr = block.nodeOffset(un_node.src_node) };
2158 }
2159 },
2160 .call, .field_call => {
2161 // A function call can never return generic poison, so we must be
2162 // evaluating an `anytype` function parameter.
2163 // TODO: better source location - function decl rather than call
2164 const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
2165 return .{ .anytype_param = block.nodeOffset(pl_node.src_node) };
2166 },
2167 else => return .unknown,
2168 }
2169 }
2170}
2171
2172pub fn analyzeAsType(
2173 sema: *Sema,
2174 block: *Block,
2175 src: LazySrcLoc,
2176 reason: std.zig.SimpleComptimeReason,
2177 air_inst: Air.Inst.Ref,
2178) !Type {
2179 const wanted_type: Type = .type;
2180 const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src);
2181 const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{ .simple = reason });
2182 return val.toType();
2183}
2184
2185pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void {
2186 const pt = sema.pt;
2187 const comp = pt.zcu.comp;
2188 const gpa = comp.gpa;
2189 const io = comp.io;
2190 const ip = &pt.zcu.intern_pool;
2191
2192 if (!comp.config.any_error_tracing) return;
2193
2194 assert(!block.isComptime());
2195 var err_trace_block = block.makeSubBlock();
2196 defer err_trace_block.instructions.deinit(gpa);
2197
2198 const src: LazySrcLoc = LazySrcLoc.unneeded;
2199
2200 // var addrs: [err_return_trace_addr_count]usize = undefined;
2201 const err_return_trace_addr_count = 32;
2202 const addr_arr_ty = try pt.arrayType(.{
2203 .len = err_return_trace_addr_count,
2204 .child = .usize_type,
2205 });
2206 const addrs_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(addr_arr_ty));
2207
2208 // var st: StackTrace = undefined;
2209 const stack_trace_ty = try sema.getStdLangType(block.nodeOffset(.zero), .StackTrace);
2210 const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty));
2211
2212 // st.instruction_addresses = &addrs;
2213 const instruction_addresses_field_name = try ip.getOrPutString(gpa, io, pt.tid, "instruction_addresses", .no_embedded_nulls);
2214 const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true);
2215 try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store);
2216
2217 // st.index = 0;
2218 const index_field_name = try ip.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
2219 const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true);
2220 try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store);
2221
2222 // @errorReturnTrace() = &st;
2223 _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr);
2224
2225 try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items);
2226}
2227
2228/// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value.
2229fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) ?Value {
2230 const zcu = sema.pt.zcu;
2231 assert(inst != .none);
2232
2233 if (inst.toInterned()) |ip_index| {
2234 return .fromInterned(ip_index);
2235 }
2236
2237 // Runtime-known value. We'll be returning `null`, but first, some assertions.
2238 const air_tags = sema.air_instructions.items(.tag);
2239 switch (air_tags[@backingInt(inst.toIndex().?)]) {
2240 .inferred_alloc => unreachable, // assertion failure
2241 .inferred_alloc_comptime => unreachable, // assertion failure
2242 else => {},
2243 }
2244 // LLVM fails to eliminate this `classify` call in -Ofast, which hurts performance, so
2245 // we must explicitly check for `std.debug.runtime_safety`.
2246 if (std.debug.runtime_safety) switch (sema.typeOf(inst).classify(zcu)) {
2247 .no_possible_value => unreachable, // values of this type do not exist
2248 .one_possible_value => unreachable, // the value should be comptime-known
2249 .partially_comptime => unreachable, // the value should be comptime-known
2250 .fully_comptime => unreachable, // the value should be comptime-known
2251 .runtime => {},
2252 };
2253 return null;
2254}
2255
2256/// Like `resolveValue`, but emits an error if the value is not comptime-known.
2257pub fn resolveConstValue(
2258 sema: *Sema,
2259 block: *Block,
2260 src: LazySrcLoc,
2261 inst: Air.Inst.Ref,
2262 /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value
2263 /// being comptime-resolved is that the block is being comptime-evaluated.
2264 reason: ?ComptimeReason,
2265) CompileError!Value {
2266 assert(reason != null or block.isComptime());
2267 return sema.resolveValue(inst) orelse {
2268 return sema.failWithNeededComptime(block, src, reason);
2269 };
2270}
2271
2272/// Like `resolveValue`, but emits an error if the value is comptime-known to be undefined.
2273fn resolveDefinedValue(
2274 sema: *Sema,
2275 block: *Block,
2276 src: LazySrcLoc,
2277 air_ref: Air.Inst.Ref,
2278) CompileError!?Value {
2279 const pt = sema.pt;
2280 const zcu = pt.zcu;
2281 const val = sema.resolveValue(air_ref) orelse return null;
2282 if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null);
2283 return val;
2284}
2285
2286/// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined.
2287pub fn resolveConstDefinedValue(
2288 sema: *Sema,
2289 block: *Block,
2290 src: LazySrcLoc,
2291 air_ref: Air.Inst.Ref,
2292 /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value
2293 /// being comptime-resolved is that the block is being comptime-evaluated.
2294 reason: ?ComptimeReason,
2295) CompileError!Value {
2296 const val = try sema.resolveConstValue(block, src, air_ref, reason);
2297 if (val.isUndef(sema.pt.zcu)) return sema.failWithUseOfUndef(block, src, null);
2298 return val;
2299}
2300
2301/// Value Tag may be `undef` or `variable`.
2302pub fn resolveFinalDeclValue(
2303 sema: *Sema,
2304 block: *Block,
2305 src: LazySrcLoc,
2306 air_ref: Air.Inst.Ref,
2307) CompileError!Value {
2308 const zcu = sema.pt.zcu;
2309 const val = try sema.resolveConstValue(block, src, air_ref, .{ .simple = .container_var_init });
2310 if (val.canMutateComptimeVarState(zcu)) {
2311 const ip = &zcu.intern_pool;
2312 const nav = ip.getNav(sema.owner.unwrap().nav_val);
2313 return sema.failWithContainsReferenceToComptimeVar(block, src, nav.name, "global variable", val);
2314 }
2315 return val;
2316}
2317
2318fn failWithNeededComptime(
2319 sema: *Sema,
2320 block: *Block,
2321 src: LazySrcLoc,
2322 /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value
2323 /// being comptime-resolved is that the block is being comptime-evaluated.
2324 reason: ?ComptimeReason,
2325) CompileError {
2326 const msg, const fail_block = msg: {
2327 const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
2328 errdefer msg.destroy(sema.gpa);
2329 const fail_block = if (reason) |r| b: {
2330 try r.explain(sema, src, msg);
2331 break :b block;
2332 } else b: {
2333 break :b try block.explainWhyBlockIsComptime(msg);
2334 };
2335 break :msg .{ msg, fail_block };
2336 };
2337 return sema.failWithOwnedErrorMsg(fail_block, msg);
2338}
2339
2340pub fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc, vector_index: ?usize) CompileError {
2341 return sema.failWithOwnedErrorMsg(block, msg: {
2342 const msg = try sema.errMsg(src, "use of undefined value here causes illegal behavior", .{});
2343 errdefer msg.destroy(sema.gpa);
2344 if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i});
2345 break :msg msg;
2346 });
2347}
2348
2349pub fn failWithUndefSliceLen(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
2350 return sema.fail(block, src, "use of slice with undefined length here causes illegal behavior", .{});
2351}
2352
2353pub fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
2354 return sema.fail(block, src, "division by zero here causes illegal behavior", .{});
2355}
2356
2357pub fn failWithTooLargeShiftAmount(
2358 sema: *Sema,
2359 block: *Block,
2360 operand_ty: Type,
2361 shift_amt: Value,
2362 shift_src: LazySrcLoc,
2363 vector_index: ?usize,
2364) CompileError {
2365 return sema.failWithOwnedErrorMsg(block, msg: {
2366 const msg = try sema.errMsg(
2367 shift_src,
2368 "shift amount '{f}' is too large for operand type '{f}'",
2369 .{ shift_amt.fmtValueSema(sema.pt, sema), operand_ty.fmt(sema.pt) },
2370 );
2371 errdefer msg.destroy(sema.gpa);
2372 if (vector_index) |i| try sema.errNote(shift_src, msg, "when computing vector element at index '{d}'", .{i});
2373 break :msg msg;
2374 });
2375}
2376
2377pub fn failWithNegativeShiftAmount(sema: *Sema, block: *Block, src: LazySrcLoc, shift_amt: Value, vector_index: ?usize) CompileError {
2378 return sema.failWithOwnedErrorMsg(block, msg: {
2379 const msg = try sema.errMsg(src, "shift by negative amount '{f}'", .{shift_amt.fmtValueSema(sema.pt, sema)});
2380 errdefer msg.destroy(sema.gpa);
2381 if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i});
2382 break :msg msg;
2383 });
2384}
2385
2386pub fn failWithUnsupportedComptimeShiftAmount(sema: *Sema, block: *Block, src: LazySrcLoc, vector_index: ?usize) CompileError {
2387 return sema.failWithOwnedErrorMsg(block, msg: {
2388 const msg = try sema.errMsg(
2389 src,
2390 "this implementation only supports comptime shift amounts of up to 2^{d} - 1 bits",
2391 .{@min(@bitSizeOf(usize), 64)},
2392 );
2393 errdefer msg.destroy(sema.gpa);
2394 if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i});
2395 break :msg msg;
2396 });
2397}
2398
2399fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError {
2400 const pt = sema.pt;
2401 return sema.fail(block, src, "remainder division with '{f}' and '{f}': signed integers and floats must use @rem or @mod", .{
2402 lhs_ty.fmt(pt), rhs_ty.fmt(pt),
2403 });
2404}
2405
2406fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src: LazySrcLoc, operand_ty: Type) CompileError {
2407 const pt = sema.pt;
2408 const zcu = pt.zcu;
2409
2410 if (operand_ty.zigTypeTag(zcu) == .@"union") {
2411 assert(operand_ty.containerLayout(zcu) == .@"packed");
2412 return sema.failWithOwnedErrorMsg(block, msg: {
2413 const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of packed union", .{});
2414 errdefer msg.destroy(sema.gpa);
2415 try sema.addDeclaredHereNote(msg, operand_ty);
2416 if (operand_ty.srcLocOrNull(zcu)) |ty_src| {
2417 try sema.errNote(ty_src, msg, "consider using a tagged union", .{});
2418 }
2419 break :msg msg;
2420 });
2421 }
2422 return sema.failWithOwnedErrorMsg(block, msg: {
2423 const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of non-union type '{f}'", .{
2424 operand_ty.fmt(pt),
2425 });
2426 errdefer msg.destroy(sema.gpa);
2427 try sema.addDeclaredHereNote(msg, operand_ty);
2428 break :msg msg;
2429 });
2430}
2431
2432fn failWithAmbiguousBackingIntType(
2433 sema: *Sema,
2434 block: *Block,
2435 src: LazySrcLoc,
2436 int_backed_ty: Type,
2437 builtin_name: []const u8,
2438) CompileError {
2439 const pt = sema.pt;
2440 const zcu = pt.zcu;
2441 return sema.failWithOwnedErrorMsg(block, msg: {
2442 const msg = try sema.errMsg(src, "{s} is ambiguous for type '{f}'", .{
2443 builtin_name, int_backed_ty.fmt(pt),
2444 });
2445 errdefer msg.destroy(sema.gpa);
2446 try sema.errNote(int_backed_ty.srcLoc(zcu), msg, "backing integer type of {t} is inferred", .{
2447 int_backed_ty.zigTypeTag(zcu),
2448 });
2449 try sema.errNote(int_backed_ty.srcLoc(zcu), msg, "consider explicitly specifying the backing integer type", .{});
2450 break :msg msg;
2451 });
2452}
2453
2454fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError {
2455 const pt = sema.pt;
2456 const msg = msg: {
2457 const msg = try sema.errMsg(src, "expected optional type, found '{f}'", .{
2458 non_optional_ty.fmt(pt),
2459 });
2460 errdefer msg.destroy(sema.gpa);
2461 if (non_optional_ty.zigTypeTag(pt.zcu) == .error_union) {
2462 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
2463 }
2464 try addDeclaredHereNote(sema, msg, non_optional_ty);
2465 break :msg msg;
2466 };
2467 return sema.failWithOwnedErrorMsg(block, msg);
2468}
2469
2470fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
2471 const pt = sema.pt;
2472 const zcu = pt.zcu;
2473 const msg = msg: {
2474 const msg = try sema.errMsg(src, "type '{f}' does not support array initialization syntax", .{
2475 ty.fmt(pt),
2476 });
2477 errdefer msg.destroy(sema.gpa);
2478 if (ty.isSlice(zcu)) {
2479 try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.childType(zcu).fmt(pt)});
2480 }
2481 break :msg msg;
2482 };
2483 return sema.failWithOwnedErrorMsg(block, msg);
2484}
2485
2486fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError {
2487 const pt = sema.pt;
2488 return sema.fail(block, src, "type '{f}' does not support struct initialization syntax", .{
2489 ty.fmt(pt),
2490 });
2491}
2492
2493pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError {
2494 const pt = sema.pt;
2495 return sema.failWithOwnedErrorMsg(block, msg: {
2496 const msg = try sema.errMsg(src, "overflow of integer type '{f}' with value '{f}'", .{
2497 int_ty.fmt(pt), val.fmtValueSema(pt, sema),
2498 });
2499 errdefer msg.destroy(sema.gpa);
2500 if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i});
2501 break :msg msg;
2502 });
2503}
2504
2505fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError {
2506 const pt = sema.pt;
2507 const zcu = pt.zcu;
2508 const msg = msg: {
2509 const msg = try sema.errMsg(init_src, "value stored in comptime field does not match the default value of the field", .{});
2510 errdefer msg.destroy(sema.gpa);
2511
2512 const struct_type = zcu.typeToStruct(container_ty) orelse break :msg msg;
2513 try sema.errNote(.{
2514 .base_node_inst = struct_type.zir_index,
2515 .offset = .{ .container_field_value = @intCast(field_index) },
2516 }, msg, "default value set here", .{});
2517 break :msg msg;
2518 };
2519 return sema.failWithOwnedErrorMsg(block, msg);
2520}
2521
2522fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError {
2523 const msg = msg: {
2524 const msg = try sema.errMsg(src, "async has not been implemented in the self-hosted compiler yet", .{});
2525 errdefer msg.destroy(sema.gpa);
2526 break :msg msg;
2527 };
2528 return sema.failWithOwnedErrorMsg(block, msg);
2529}
2530
2531fn failWithInvalidFieldAccess(
2532 sema: *Sema,
2533 block: *Block,
2534 src: LazySrcLoc,
2535 object_ty: Type,
2536 field_name: InternPool.NullTerminatedString,
2537) CompileError {
2538 const pt = sema.pt;
2539 const zcu = pt.zcu;
2540 const inner_ty = if (object_ty.isSinglePointer(zcu)) object_ty.childType(zcu) else object_ty;
2541
2542 if (inner_ty.zigTypeTag(zcu) == .optional) opt: {
2543 const child_ty = inner_ty.optionalChild(zcu);
2544 if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :opt;
2545 const msg = msg: {
2546 const msg = try sema.errMsg(src, "optional type '{f}' does not support field access", .{object_ty.fmt(pt)});
2547 errdefer msg.destroy(sema.gpa);
2548 try sema.errNote(src, msg, "consider using '.?', 'orelse', or 'if'", .{});
2549 break :msg msg;
2550 };
2551 return sema.failWithOwnedErrorMsg(block, msg);
2552 } else if (inner_ty.zigTypeTag(zcu) == .error_union) err: {
2553 const child_ty = inner_ty.errorUnionPayload(zcu);
2554 if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :err;
2555 const msg = msg: {
2556 const msg = try sema.errMsg(src, "error union type '{f}' does not support field access", .{object_ty.fmt(pt)});
2557 errdefer msg.destroy(sema.gpa);
2558 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
2559 break :msg msg;
2560 };
2561 return sema.failWithOwnedErrorMsg(block, msg);
2562 }
2563 return sema.fail(block, src, "type '{f}' does not support field access", .{object_ty.fmt(pt)});
2564}
2565
2566fn typeSupportsFieldAccess(zcu: *const Zcu, ty: Type, field_name: InternPool.NullTerminatedString) bool {
2567 const ip = &zcu.intern_pool;
2568 switch (ty.zigTypeTag(zcu)) {
2569 .array => return field_name.eqlSlice("len", ip),
2570 .pointer => {
2571 const ptr_info = ty.ptrInfo(zcu);
2572 if (ptr_info.flags.size == .slice) {
2573 return field_name.eqlSlice("ptr", ip) or field_name.eqlSlice("len", ip);
2574 } else if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) {
2575 return field_name.eqlSlice("len", ip);
2576 } else return false;
2577 },
2578 .type, .@"struct", .@"union" => return true,
2579 else => return false,
2580 }
2581}
2582
2583fn failWithComptimeErrorRetTrace(
2584 sema: *Sema,
2585 block: *Block,
2586 src: LazySrcLoc,
2587 name: InternPool.NullTerminatedString,
2588) CompileError {
2589 const pt = sema.pt;
2590 const zcu = pt.zcu;
2591 const msg = msg: {
2592 const msg = try sema.errMsg(src, "caught unexpected error '{f}'", .{name.fmt(&zcu.intern_pool)});
2593 errdefer msg.destroy(sema.gpa);
2594
2595 for (sema.comptime_err_ret_trace.items) |src_loc| {
2596 try sema.errNote(src_loc, msg, "error returned here", .{});
2597 }
2598 break :msg msg;
2599 };
2600 return sema.failWithOwnedErrorMsg(block, msg);
2601}
2602
2603fn failWithInvalidPtrArithmetic(sema: *Sema, block: *Block, src: LazySrcLoc, arithmetic: []const u8, supports: []const u8) CompileError {
2604 const msg = msg: {
2605 const msg = try sema.errMsg(src, "invalid {s} arithmetic operator", .{arithmetic});
2606 errdefer msg.destroy(sema.gpa);
2607 try sema.errNote(src, msg, "{s} arithmetic only supports {s}", .{ arithmetic, supports });
2608 break :msg msg;
2609 };
2610 return sema.failWithOwnedErrorMsg(block, msg);
2611}
2612
2613/// We don't return a pointer to the new error note because the pointer
2614/// becomes invalid when you add another one.
2615pub fn errNote(
2616 sema: *Sema,
2617 src: LazySrcLoc,
2618 parent: *Zcu.ErrorMsg,
2619 comptime format: []const u8,
2620 args: anytype,
2621) error{OutOfMemory}!void {
2622 return sema.pt.zcu.errNote(src, parent, format, args);
2623}
2624
2625fn addFieldErrNote(
2626 sema: *Sema,
2627 container_ty: Type,
2628 field_index: usize,
2629 parent: *Zcu.ErrorMsg,
2630 comptime format: []const u8,
2631 args: anytype,
2632) !void {
2633 @branchHint(.cold);
2634 const type_src = container_ty.srcLocOrNull(sema.pt.zcu) orelse return;
2635 const field_src: LazySrcLoc = .{
2636 .base_node_inst = type_src.base_node_inst,
2637 .offset = .{ .container_field_name = @intCast(field_index) },
2638 };
2639 try sema.errNote(field_src, parent, format, args);
2640}
2641
2642pub fn errMsg(
2643 sema: *Sema,
2644 src: LazySrcLoc,
2645 comptime format: []const u8,
2646 args: anytype,
2647) Allocator.Error!*Zcu.ErrorMsg {
2648 assert(src.offset != .unneeded);
2649 return Zcu.ErrorMsg.create(sema.gpa, src, format, args);
2650}
2651
2652fn typeMismatchErrMsg(sema: *Sema, src: LazySrcLoc, expected: Type, found: Type) Allocator.Error!*Zcu.ErrorMsg {
2653 const pt = sema.pt;
2654 var cmp: Type.Comparison = try .init(&.{ expected, found }, pt);
2655 defer cmp.deinit(pt);
2656
2657 const msg = try sema.errMsg(src, "expected type '{f}', found '{f}'", .{
2658 cmp.fmtType(expected, pt),
2659 cmp.fmtType(found, pt),
2660 });
2661 errdefer msg.destroy(sema.gpa);
2662
2663 for (cmp.type_dedupe_cache.keys(), cmp.type_dedupe_cache.values()) |ty, value| {
2664 if (value == .dont_dedupe) continue;
2665 const placeholder = value.dedupe;
2666 try sema.errNote(src, msg, "{f} = {f}", .{ placeholder, ty.fmt(pt) });
2667 }
2668
2669 return msg;
2670}
2671
2672pub fn fail(
2673 sema: *Sema,
2674 block: *Block,
2675 src: LazySrcLoc,
2676 comptime format: []const u8,
2677 args: anytype,
2678) SemaError {
2679 const err_msg = try sema.errMsg(src, format, args);
2680 inline for (args) |arg| {
2681 if (@TypeOf(arg) == Type.Formatter) {
2682 try addDeclaredHereNote(sema, err_msg, arg.data.ty);
2683 }
2684 }
2685 return sema.failWithOwnedErrorMsg(block, err_msg);
2686}
2687
2688fn failWithTypeMismatch(sema: *Sema, block: *Block, src: LazySrcLoc, expected: Type, found: Type) CompileError {
2689 return sema.failWithOwnedErrorMsg(block, msg: {
2690 const msg = try sema.typeMismatchErrMsg(src, expected, found);
2691 errdefer msg.destroy(sema.gpa);
2692 try addDeclaredHereNote(sema, msg, expected);
2693 try addDeclaredHereNote(sema, msg, found);
2694 break :msg msg;
2695 });
2696}
2697
2698pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) SemaError {
2699 @branchHint(.cold);
2700 const zcu = sema.pt.zcu;
2701 const comp = zcu.comp;
2702 const gpa = comp.gpa;
2703 const io = comp.io;
2704
2705 assert(sema.err == null);
2706
2707 if (build_options.enable_debug_extensions and comp.debug_compile_errors) {
2708 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
2709 wip_errors.init(gpa) catch @panic("out of memory");
2710 Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory");
2711 std.debug.print("compile error during Sema:\n", .{});
2712 var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory");
2713 error_bundle.renderToStderr(io, .{}, .auto) catch @panic("failed to print to stderr");
2714 std.debug.panicExtra(@returnAddress(), "unexpected compile error occurred", .{});
2715 }
2716
2717 if (block) |start_block| {
2718 var block_it = start_block;
2719 while (block_it.inlining) |inlining| {
2720 const note_str = note: {
2721 if (inlining.is_generic_instantiation) break :note "generic function instantiated here";
2722 if (inlining.call_block.isComptime()) break :note "called at comptime here";
2723 break :note "called inline here";
2724 };
2725 try sema.errNote(inlining.call_src, err_msg, "{s}", .{note_str});
2726 block_it = inlining.call_block;
2727 }
2728 }
2729
2730 err_msg.reference_trace_root = sema.owner.toOptional();
2731
2732 try zcu.failed_analysis.putNoClobber(gpa, sema.owner, err_msg);
2733 assert(!zcu.transitive_failed_analysis.contains(sema.owner));
2734
2735 sema.err = err_msg;
2736 return error.AlreadyReported;
2737}
2738
2739/// Given an ErrorMsg, modify its message and source location to the given values, turning the
2740/// original message into a note. Notes on the original message are preserved as further notes.
2741/// Reference trace is preserved.
2742fn reparentOwnedErrorMsg(
2743 sema: *Sema,
2744 src: LazySrcLoc,
2745 msg: *Zcu.ErrorMsg,
2746 comptime format: []const u8,
2747 args: anytype,
2748) !void {
2749 const msg_str = try std.fmt.allocPrint(sema.gpa, format, args);
2750
2751 const orig_notes = msg.notes.len;
2752 msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1);
2753 @memmove(msg.notes[1..][0..orig_notes], msg.notes[0..orig_notes]);
2754 msg.notes[0] = .{
2755 .src_loc = msg.src_loc,
2756 .msg = msg.msg,
2757 };
2758
2759 msg.src_loc = src;
2760 msg.msg = msg_str;
2761}
2762
2763const align_ty: Type = .u29;
2764
2765pub fn analyzeAsAlign(
2766 sema: *Sema,
2767 block: *Block,
2768 src: LazySrcLoc,
2769 air_ref: Air.Inst.Ref,
2770) !Alignment {
2771 const alignment_big = try sema.analyzeAsInt(
2772 block,
2773 src,
2774 air_ref,
2775 align_ty,
2776 .{ .simple = .@"align" },
2777 );
2778 return sema.validateAlign(block, src, alignment_big);
2779}
2780
2781fn validateAlign(
2782 sema: *Sema,
2783 block: *Block,
2784 src: LazySrcLoc,
2785 alignment: u64,
2786) !Alignment {
2787 if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{});
2788 if (!std.math.isPowerOfTwo(alignment)) {
2789 return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{
2790 alignment,
2791 });
2792 }
2793 return Alignment.fromNonzeroByteUnits(alignment);
2794}
2795
2796fn resolveAlign(
2797 sema: *Sema,
2798 block: *Block,
2799 src: LazySrcLoc,
2800 zir_ref: Zir.Inst.Ref,
2801) !Alignment {
2802 const air_ref = sema.resolveInst(zir_ref);
2803 return sema.analyzeAsAlign(block, src, air_ref);
2804}
2805
2806fn resolveInt(
2807 sema: *Sema,
2808 block: *Block,
2809 src: LazySrcLoc,
2810 zir_ref: Zir.Inst.Ref,
2811 dest_ty: Type,
2812 reason: ComptimeReason,
2813) !u64 {
2814 const air_ref = sema.resolveInst(zir_ref);
2815 return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason);
2816}
2817
2818fn analyzeAsInt(
2819 sema: *Sema,
2820 block: *Block,
2821 src: LazySrcLoc,
2822 air_ref: Air.Inst.Ref,
2823 dest_ty: Type,
2824 reason: ComptimeReason,
2825) !u64 {
2826 const coerced = try sema.coerce(block, dest_ty, air_ref, src);
2827 const val = try sema.resolveConstDefinedValue(block, src, coerced, reason);
2828 return val.toUnsignedInt(sema.pt.zcu);
2829}
2830
2831fn analyzeValueAsCallconv(
2832 sema: *Sema,
2833 block: *Block,
2834 src: LazySrcLoc,
2835 val: Value,
2836) !std.lang.CallingConvention {
2837 return interpretStdLangType(sema, block, src, val, std.lang.CallingConvention);
2838}
2839
2840fn interpretStdLangType(
2841 sema: *Sema,
2842 block: *Block,
2843 src: LazySrcLoc,
2844 val: Value,
2845 comptime T: type,
2846) !T {
2847 return val.interpret(T, sema.pt) catch |err| switch (err) {
2848 error.OutOfMemory => |e| return e,
2849 error.UndefinedValue => return sema.failWithUseOfUndef(block, src, null),
2850 error.TypeMismatch => @panic("std.lang is corrupt"),
2851 };
2852}
2853
2854fn uninterpretStdLangType(
2855 sema: *Sema,
2856 val: anytype,
2857 ty: Type,
2858) !Value {
2859 return Value.uninterpret(val, ty, sema.pt) catch |err| switch (err) {
2860 error.OutOfMemory => |e| return e,
2861 error.TypeMismatch => @panic("std.lang is corrupt"),
2862 };
2863}
2864
2865fn zirTupleDecl(
2866 sema: *Sema,
2867 block: *Block,
2868 extended: Zir.Inst.Extended.InstData,
2869) CompileError!Air.Inst.Ref {
2870 const pt = sema.pt;
2871 const zcu = pt.zcu;
2872 const comp = zcu.comp;
2873 const gpa = comp.gpa;
2874 const io = comp.io;
2875
2876 const fields_len = extended.small;
2877 const extra = sema.code.extraData(Zir.Inst.TupleDecl, extended.operand);
2878 var extra_index = extra.end;
2879
2880 const types = try sema.arena.alloc(InternPool.Index, fields_len);
2881 const inits = try sema.arena.alloc(InternPool.Index, fields_len);
2882
2883 const extra_as_refs: []const Zir.Inst.Ref = @ptrCast(sema.code.extra);
2884
2885 for (types, inits, 0..) |*field_ty, *field_init, field_index| {
2886 const zir_field_ty, const zir_field_init = extra_as_refs[extra_index..][0..2].*;
2887 extra_index += 2;
2888
2889 const type_src = block.src(.{ .tuple_field_type = .{
2890 .tuple_decl_node_offset = extra.data.src_node,
2891 .elem_index = @intCast(field_index),
2892 } });
2893 const init_src = block.src(.{ .tuple_field_init = .{
2894 .tuple_decl_node_offset = extra.data.src_node,
2895 .elem_index = @intCast(field_index),
2896 } });
2897
2898 const field_type = try sema.resolveType(block, type_src, zir_field_ty);
2899 try sema.validateTupleFieldType(block, field_type, type_src);
2900
2901 field_ty.* = field_type.toIntern();
2902 field_init.* = init: {
2903 if (zir_field_init != .none) {
2904 const uncoerced_field_init = sema.resolveInst(zir_field_init);
2905 const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src);
2906 const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value });
2907 if (field_init_val.canMutateComptimeVarState(zcu)) {
2908 const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, io, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
2909 return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val);
2910 }
2911 break :init field_init_val.toIntern();
2912 }
2913 break :init .none;
2914 };
2915 }
2916
2917 return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{
2918 .types = types,
2919 .values = inits,
2920 }));
2921}
2922
2923fn validateTupleFieldType(
2924 sema: *Sema,
2925 block: *Block,
2926 field_ty: Type,
2927 field_ty_src: LazySrcLoc,
2928) CompileError!void {
2929 const gpa = sema.gpa;
2930 const zcu = sema.pt.zcu;
2931 if (field_ty.zigTypeTag(zcu) == .@"opaque") {
2932 return sema.failWithOwnedErrorMsg(block, msg: {
2933 const msg = try sema.errMsg(field_ty_src, "opaque types have unknown size and therefore cannot be directly embedded in tuples", .{});
2934 errdefer msg.destroy(gpa);
2935
2936 try sema.addDeclaredHereNote(msg, field_ty);
2937 break :msg msg;
2938 });
2939 }
2940 if (field_ty.zigTypeTag(zcu) == .noreturn) {
2941 return sema.failWithOwnedErrorMsg(block, msg: {
2942 const msg = try sema.errMsg(field_ty_src, "tuple fields cannot be 'noreturn'", .{});
2943 errdefer msg.destroy(gpa);
2944
2945 try sema.addDeclaredHereNote(msg, field_ty);
2946 break :msg msg;
2947 });
2948 }
2949}
2950
2951/// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`,
2952/// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`.
2953fn getCaptures(
2954 sema: *Sema,
2955 block: *Block,
2956 type_src: LazySrcLoc,
2957 zir_captures: []const Zir.Inst.Capture,
2958 zir_capture_names: []const Zir.NullTerminatedString,
2959) ![]InternPool.CaptureValue {
2960 const pt = sema.pt;
2961 const zcu = pt.zcu;
2962 const comp = zcu.comp;
2963 const gpa = comp.gpa;
2964 const io = comp.io;
2965 const ip = &zcu.intern_pool;
2966
2967 const parent_ty: Type = .fromInterned(zcu.namespacePtr(block.namespace).owner_type);
2968 const parent_captures: InternPool.CaptureValue.Slice = parent_ty.getCaptures(zcu);
2969
2970 const captures = try sema.arena.alloc(InternPool.CaptureValue, zir_captures.len);
2971
2972 for (zir_captures, zir_capture_names, captures) |zir_capture, zir_name, *capture| {
2973 const zir_name_slice = sema.code.nullTerminatedString(zir_name);
2974 capture.* = switch (zir_capture.unwrap()) {
2975 .nested => |parent_idx| parent_captures.get(ip)[parent_idx],
2976 .instruction_load => |ptr_inst| capture: {
2977 const ptr_ref = sema.resolveInst(ptr_inst.toRef());
2978 const ptr_val = sema.resolveValue(ptr_ref) orelse {
2979 break :capture .wrap(.{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() });
2980 };
2981 // TODO: better source location
2982 const loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse {
2983 break :capture .wrap(.{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() });
2984 };
2985 if (loaded_val.canMutateComptimeVarState(zcu)) {
2986 const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls);
2987 return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", loaded_val);
2988 }
2989 break :capture .wrap(.{ .@"comptime" = loaded_val.toIntern() });
2990 },
2991 .instruction => |inst| capture: {
2992 const air_ref = sema.resolveInst(inst.toRef());
2993 if (sema.resolveValue(air_ref)) |val| {
2994 if (val.canMutateComptimeVarState(zcu)) {
2995 const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls);
2996 return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", val);
2997 }
2998 break :capture .wrap(.{ .@"comptime" = val.toIntern() });
2999 }
3000 break :capture .wrap(.{ .runtime = sema.typeOf(air_ref).toIntern() });
3001 },
3002 .decl_val => |str| capture: {
3003 const decl_name = try ip.getOrPutString(
3004 gpa,
3005 io,
3006 pt.tid,
3007 sema.code.nullTerminatedString(str),
3008 .no_embedded_nulls,
3009 );
3010 const nav = try sema.lookupIdentifier(block, decl_name);
3011 break :capture .wrap(.{ .nav_val = nav });
3012 },
3013 .decl_ref => |str| capture: {
3014 const decl_name = try ip.getOrPutString(
3015 gpa,
3016 io,
3017 pt.tid,
3018 sema.code.nullTerminatedString(str),
3019 .no_embedded_nulls,
3020 );
3021 const nav = try sema.lookupIdentifier(block, decl_name);
3022 break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav });
3023 },
3024 };
3025 }
3026
3027 return captures;
3028}
3029
3030fn zirErrorSetDecl(
3031 sema: *Sema,
3032 inst: Zir.Inst.Index,
3033) CompileError!Air.Inst.Ref {
3034 const pt = sema.pt;
3035 const zcu = pt.zcu;
3036 const comp = zcu.comp;
3037 const gpa = comp.gpa;
3038 const io = comp.io;
3039
3040 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
3041 const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index);
3042
3043 var names: InferredErrorSet.NameMap = .{};
3044 try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len);
3045
3046 var extra_index: u32 = @intCast(extra.end);
3047 const extra_index_end = extra_index + extra.data.fields_len;
3048 while (extra_index < extra_index_end) : (extra_index += 1) {
3049 const name_index: Zir.NullTerminatedString = @fromBackingInt(@intCast(sema.code.extra[extra_index]));
3050 const name = sema.code.nullTerminatedString(name_index);
3051 const name_ip = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
3052 _ = try pt.getErrorValue(name_ip);
3053 const result = names.getOrPutAssumeCapacity(name_ip);
3054 assert(!result.found_existing); // verified in AstGen
3055 }
3056
3057 return Air.internedToRef((try pt.errorSetFromUnsortedNames(names.keys())).toIntern());
3058}
3059
3060fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3061 const pt = sema.pt;
3062 const zcu = pt.zcu;
3063
3064 const src = block.nodeOffset(sema.code.instructions.items(.data)[@backingInt(inst)].node);
3065
3066 if (block.isComptime() or sema.fn_ret_ty.comptimeOnly(zcu)) {
3067 return sema.analyzeComptimeAlloc(block, src, sema.fn_ret_ty, .none);
3068 }
3069
3070 const target = zcu.getTarget();
3071 const ptr_type = try pt.ptrType(.{
3072 .child = sema.fn_ret_ty.toIntern(),
3073 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
3074 });
3075
3076 if (block.inlining != null) {
3077 // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr.
3078 // TODO when functions gain result location support, the inlining struct in
3079 // Block should contain the return pointer, and we would pass that through here.
3080 return block.addTy(.alloc, ptr_type);
3081 }
3082
3083 return block.addTy(.ret_ptr, ptr_type);
3084}
3085
3086fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3087 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_tok;
3088 const operand = sema.resolveInst(inst_data.operand);
3089 return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand, .none);
3090}
3091
3092fn zirDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3093 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3094 const src = block.nodeOffset(inst_data.src_node);
3095 const ptr_src = block.src(.{ .node_offset_deref_ptr = inst_data.src_node });
3096 const operand = sema.resolveInst(inst_data.operand);
3097
3098 try sema.validateDeref(block, src, operand, sema.typeOf(operand));
3099
3100 return sema.analyzeLoad(block, src, operand, ptr_src);
3101}
3102
3103fn zirRefDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3104 const pt = sema.pt;
3105 const zcu = pt.zcu;
3106 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3107 const src = block.nodeOffset(inst_data.src_node);
3108 const operand = sema.resolveInst(inst_data.operand);
3109 const operand_ty = sema.typeOf(operand);
3110
3111 try sema.validateDeref(block, src, operand, operand_ty);
3112
3113 const ptr_info = operand_ty.ptrInfo(zcu);
3114 return single_ptr: switch (ptr_info.flags.size) {
3115 .many => unreachable, // cannot be dereferenced directly
3116 .slice => {
3117 const slice_val = sema.resolveValue(operand).?;
3118 const slice = zcu.intern_pool.indexToKey(slice_val.toIntern()).slice;
3119 break :single_ptr .fromValue(try pt.sliceToArrayPtr(slice));
3120 },
3121 .c => {
3122 const single_ptr_ty = try pt.ptrType(p: {
3123 var p = ptr_info;
3124 p.flags.size = .one;
3125 p.flags.is_allowzero = false;
3126 break :p p;
3127 });
3128 // https://github.com/ziglang/zig/issues/6597
3129 if (sema.resolveValue(operand)) |operand_val| {
3130 if (!operand_val.isNull(zcu)) {
3131 break :single_ptr .fromValue(try pt.getCoerced(operand_val, single_ptr_ty));
3132 }
3133 }
3134 if (block.wantSafety()) {
3135 const is_non_null = try block.addUnOp(.is_non_null, operand);
3136 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
3137 }
3138 const single_ptr = try block.addTyOp(.ptr_cast, single_ptr_ty, operand);
3139 try sema.checkKnownAllocPtr(block, operand, single_ptr);
3140 break :single_ptr single_ptr;
3141 },
3142 .one => operand,
3143 };
3144}
3145
3146fn validateDeref(
3147 sema: *Sema,
3148 block: *Block,
3149 src: LazySrcLoc,
3150 ref: Air.Inst.Ref,
3151 ty: Type,
3152) CompileError!void {
3153 const pt = sema.pt;
3154 const zcu = pt.zcu;
3155 const ip = &zcu.intern_pool;
3156 if (ty.zigTypeTag(zcu) != .pointer) {
3157 return sema.fail(block, src, "cannot dereference non-pointer type '{f}'", .{ty.fmt(pt)});
3158 }
3159 const size = ty.ptrSize(zcu);
3160 switch (size) {
3161 .many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{f}'", .{ty.fmt(pt)}),
3162 .one, .c, .slice => {},
3163 }
3164 if (sema.resolveValue(ref)) |val| {
3165 // Error for deref of undef pointer, unless the pointee is OPV in which case it's legal.
3166 if (val.isUndef(zcu) and ty.childType(zcu).classify(zcu) != .one_possible_value) {
3167 return sema.fail(block, src, "cannot dereference undefined value", .{});
3168 }
3169 // We need a defined slice length for the array type the slice should be dereferenced to.
3170 if (size == .slice and ip.indexToKey(val.toIntern()).slice.len == .undef_usize) {
3171 return sema.fail(block, src, "cannot dereference slice with undefined length", .{});
3172 }
3173 } else if (size == .slice) {
3174 return sema.fail(block, src, "index syntax required to access runtime-known slice", .{});
3175 }
3176}
3177
3178fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
3179 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3180 const operand = sema.resolveInst(inst_data.operand);
3181 const src = block.nodeOffset(inst_data.src_node);
3182
3183 return sema.ensureResultUsed(block, sema.typeOf(operand), src);
3184}
3185
3186fn ensureResultUsed(
3187 sema: *Sema,
3188 block: *Block,
3189 ty: Type,
3190 src: LazySrcLoc,
3191) CompileError!void {
3192 const pt = sema.pt;
3193 const zcu = pt.zcu;
3194 switch (ty.zigTypeTag(zcu)) {
3195 .void, .noreturn => return,
3196 .error_set => {
3197 return sema.fail(block, src, "error set of type '{f}' is ignored", .{ty.fmt(pt)});
3198 },
3199 .error_union => {
3200 const msg = msg: {
3201 const msg = try sema.errMsg(src, "error union of type '{f}' is ignored", .{ty.fmt(pt)});
3202 errdefer msg.destroy(sema.gpa);
3203 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
3204 break :msg msg;
3205 };
3206 return sema.failWithOwnedErrorMsg(block, msg);
3207 },
3208 else => {
3209 const msg = msg: {
3210 const msg = try sema.errMsg(src, "value of type '{f}' ignored", .{ty.fmt(pt)});
3211 errdefer msg.destroy(sema.gpa);
3212 try sema.errNote(src, msg, "all non-void values must be used", .{});
3213 try sema.errNote(src, msg, "to discard the value, assign it to '_'", .{});
3214 break :msg msg;
3215 };
3216 return sema.failWithOwnedErrorMsg(block, msg);
3217 },
3218 }
3219}
3220
3221fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
3222 const pt = sema.pt;
3223 const zcu = pt.zcu;
3224 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3225 const operand = sema.resolveInst(inst_data.operand);
3226 const src = block.nodeOffset(inst_data.src_node);
3227 const operand_ty = sema.typeOf(operand);
3228 switch (operand_ty.zigTypeTag(zcu)) {
3229 .error_set => return sema.fail(block, src, "error set is discarded", .{}),
3230 .error_union => {
3231 const msg = msg: {
3232 const msg = try sema.errMsg(src, "error union is discarded", .{});
3233 errdefer msg.destroy(sema.gpa);
3234 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
3235 break :msg msg;
3236 };
3237 return sema.failWithOwnedErrorMsg(block, msg);
3238 },
3239 else => return,
3240 }
3241}
3242
3243fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
3244 const pt = sema.pt;
3245 const zcu = pt.zcu;
3246 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3247 const src = block.nodeOffset(inst_data.src_node);
3248 const operand = sema.resolveInst(inst_data.operand);
3249 const operand_ty = sema.typeOf(operand);
3250 const err_union_ty = if (operand_ty.zigTypeTag(zcu) == .pointer)
3251 operand_ty.childType(zcu)
3252 else
3253 operand_ty;
3254 if (err_union_ty.zigTypeTag(zcu) != .error_union) return;
3255 const payload_ty = err_union_ty.errorUnionPayload(zcu).zigTypeTag(zcu);
3256 if (payload_ty != .void and payload_ty != .noreturn) {
3257 const msg = msg: {
3258 const msg = try sema.errMsg(src, "error union payload is ignored", .{});
3259 errdefer msg.destroy(sema.gpa);
3260 try sema.errNote(src, msg, "payload value can be explicitly ignored with '|_|'", .{});
3261 break :msg msg;
3262 };
3263 return sema.failWithOwnedErrorMsg(block, msg);
3264 }
3265}
3266
3267fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3268 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3269 const src = block.nodeOffset(inst_data.src_node);
3270 const object = sema.resolveInst(inst_data.operand);
3271
3272 return indexablePtrLen(sema, block, src, object);
3273}
3274
3275fn indexablePtrLen(
3276 sema: *Sema,
3277 block: *Block,
3278 src: LazySrcLoc,
3279 object: Air.Inst.Ref,
3280) CompileError!Air.Inst.Ref {
3281 const pt = sema.pt;
3282 const zcu = pt.zcu;
3283 const comp = zcu.comp;
3284 const gpa = comp.gpa;
3285 const io = comp.io;
3286 const object_ty = sema.typeOf(object);
3287 const is_pointer_to = object_ty.isSinglePointer(zcu);
3288 const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty;
3289 try sema.checkIndexable(block, src, indexable_ty);
3290 const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls);
3291 return sema.fieldVal(block, src, object, field_name, src);
3292}
3293
3294fn indexablePtrLenOrNone(
3295 sema: *Sema,
3296 block: *Block,
3297 src: LazySrcLoc,
3298 operand: Air.Inst.Ref,
3299) CompileError!Air.Inst.Ref {
3300 const pt = sema.pt;
3301 const zcu = pt.zcu;
3302 const comp = zcu.comp;
3303 const gpa = comp.gpa;
3304 const io = comp.io;
3305 const operand_ty = sema.typeOf(operand);
3306 try checkMemOperand(sema, block, src, operand_ty);
3307 switch (operand_ty.ptrSize(zcu)) {
3308 .many, .c => return .none,
3309 .one, .slice => {},
3310 }
3311 const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls);
3312 return sema.fieldVal(block, src, operand, field_name, src);
3313}
3314
3315fn zirAllocExtended(
3316 sema: *Sema,
3317 block: *Block,
3318 extended: Zir.Inst.Extended.InstData,
3319) CompileError!Air.Inst.Ref {
3320 const pt = sema.pt;
3321 const zcu = pt.zcu;
3322 const gpa = sema.gpa;
3323 const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand);
3324 const var_src = block.nodeOffset(extra.data.src_node);
3325 const ty_src = block.src(.{ .node_offset_var_decl_ty = extra.data.src_node });
3326 const align_src = block.src(.{ .node_offset_var_decl_align = extra.data.src_node });
3327 const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small);
3328
3329 var extra_index: usize = extra.end;
3330
3331 const var_ty: Type = if (small.has_type) blk: {
3332 const type_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index]));
3333 extra_index += 1;
3334 break :blk try sema.resolveType(block, ty_src, type_ref);
3335 } else undefined;
3336
3337 const alignment = if (small.has_align) blk: {
3338 const align_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index]));
3339 extra_index += 1;
3340 break :blk try sema.resolveAlign(block, align_src, align_ref);
3341 } else .none;
3342
3343 if (small.has_type) {
3344 try sema.ensureLayoutResolved(var_ty, var_src, if (small.is_const) .constant else .variable);
3345 if (block.isComptime() or small.is_comptime or var_ty.comptimeOnly(zcu)) {
3346 return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment);
3347 }
3348 if (!small.is_const) {
3349 try sema.validateVarType(block, ty_src, var_ty, false);
3350 }
3351 const target = pt.zcu.getTarget();
3352 if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) {
3353 const store_src = block.src(.{ .node_offset_store_ptr = extra.data.src_node });
3354 return sema.fail(block, store_src, "local variable in naked function", .{});
3355 }
3356 const ptr_type = try pt.ptrType(.{
3357 .child = var_ty.toIntern(),
3358 .flags = .{
3359 .alignment = alignment,
3360 .address_space = target_util.defaultAddressSpace(target, .local),
3361 },
3362 });
3363 const ptr = try block.addTy(.alloc, ptr_type);
3364 if (small.is_const) {
3365 const ptr_inst = ptr.toIndex().?;
3366 try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index });
3367 try sema.base_allocs.put(gpa, ptr_inst, ptr_inst);
3368 }
3369 return ptr;
3370 }
3371
3372 if (block.isComptime() or small.is_comptime) {
3373 const iac_index: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
3374 try sema.air_instructions.append(gpa, .{
3375 .tag = .inferred_alloc_comptime,
3376 .data = .{ .inferred_alloc_comptime = .{
3377 .alignment = alignment,
3378 .is_const = small.is_const,
3379 .ptr = undefined,
3380 } },
3381 });
3382 return iac_index.toRef();
3383 }
3384
3385 const result_index = try block.addInstAsIndex(.{
3386 .tag = .inferred_alloc,
3387 .data = .{ .inferred_alloc = .{
3388 .alignment = alignment,
3389 .is_const = small.is_const,
3390 } },
3391 });
3392 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
3393 if (small.is_const) {
3394 try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index });
3395 try sema.base_allocs.put(gpa, result_index, result_index);
3396 }
3397 return result_index.toRef();
3398}
3399
3400fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3401 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3402 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
3403 const var_src = block.nodeOffset(inst_data.src_node);
3404 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
3405 try sema.ensureLayoutResolved(var_ty, var_src, .variable);
3406 return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
3407}
3408
3409fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3410 const pt = sema.pt;
3411 const zcu = pt.zcu;
3412 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3413 const alloc = sema.resolveInst(inst_data.operand);
3414 const alloc_ty = sema.typeOf(alloc);
3415 const ptr_info = alloc_ty.ptrInfo(zcu);
3416 const elem_ty: Type = .fromInterned(ptr_info.child);
3417
3418 // If the alloc was created in a comptime scope, we already created a comptime alloc for it.
3419 // However, if the final constructed value does not reference comptime-mutable memory, we wish
3420 // to promote it to an anon decl.
3421 already_ct: {
3422 const ptr_val = sema.resolveValue(alloc) orelse break :already_ct;
3423
3424 // If this was a comptime inferred alloc, then `storeToInferredAllocComptime`
3425 // might have already done our job and created an anon decl ref.
3426 switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
3427 .ptr => |ptr| switch (ptr.base_addr) {
3428 .uav => {
3429 // The comptime-ification was already done for us.
3430 // Just make sure the pointer is const.
3431 return sema.makePtrConst(block, alloc);
3432 },
3433 else => {},
3434 },
3435 else => {},
3436 }
3437
3438 if (!sema.isComptimeMutablePtr(ptr_val)) break :already_ct;
3439 const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr;
3440 assert(ptr.byte_offset == 0);
3441 const alloc_index = ptr.base_addr.comptime_alloc;
3442 const ct_alloc = sema.getComptimeAlloc(alloc_index);
3443 const interned = try ct_alloc.val.intern(pt, sema.arena);
3444 if (interned.canMutateComptimeVarState(zcu)) {
3445 // Preserve the comptime alloc, just make the pointer const.
3446 ct_alloc.val = .{ .interned = interned.toIntern() };
3447 ct_alloc.is_const = true;
3448 return sema.makePtrConst(block, alloc);
3449 } else {
3450 // Promote the constant to an anon decl.
3451 const new_mut_ptr = Air.internedToRef(try pt.intern(.{ .ptr = .{
3452 .ty = alloc_ty.toIntern(),
3453 .base_addr = .{ .uav = .{
3454 .val = interned.toIntern(),
3455 .orig_ty = alloc_ty.toIntern(),
3456 } },
3457 .byte_offset = 0,
3458 } }));
3459 return sema.makePtrConst(block, new_mut_ptr);
3460 }
3461 }
3462
3463 // Otherwise, check if the alloc is comptime-known despite being in a runtime scope.
3464 if (try sema.resolveComptimeKnownAllocPtr(block, alloc, null)) |ptr_val| {
3465 return sema.makePtrConst(block, Air.internedToRef(ptr_val));
3466 }
3467
3468 if (elem_ty.comptimeOnly(zcu)) {
3469 // The value was initialized through RLS, so we didn't detect the runtime condition earlier.
3470 // TODO: source location of runtime control flow
3471 const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node });
3472 return sema.fail(block, init_src, "value with comptime-only type '{f}' depends on runtime control flow", .{elem_ty.fmt(pt)});
3473 }
3474
3475 // This is a runtime value.
3476 return sema.makePtrConst(block, alloc);
3477}
3478
3479/// If `alloc` is an inferred allocation, `resolved_inferred_ty` is taken to be its resolved
3480/// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`.
3481fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index {
3482 const pt = sema.pt;
3483 const zcu = pt.zcu;
3484 const comp = zcu.comp;
3485 const gpa = comp.gpa;
3486 const io = comp.io;
3487
3488 const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc);
3489 const ptr_info = alloc_ty.ptrInfo(zcu);
3490 const elem_ty: Type = .fromInterned(ptr_info.child);
3491 elem_ty.assertHasLayout(zcu);
3492
3493 const alloc_inst = alloc.toIndex() orelse return null;
3494 const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null;
3495 const stores = comptime_info.value.stores.items(.inst);
3496
3497 // If the elem type is OPV, no need to faff about with `stores`; just use the OPV.
3498 if (try elem_ty.onePossibleValue(pt)) |opv| {
3499 return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, opv.toIntern(), null, alloc_inst, comptime_info.value);
3500 }
3501
3502 // Since the elem type isn't OPV, there should have been at least one store.
3503 assert(stores.len > 0);
3504
3505 // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known.
3506 // We will resolve and return its value.
3507
3508 // In general, we want to create a comptime alloc of the correct type and
3509 // apply the stores to that alloc in order. However, before going to all
3510 // that effort, let's optimize for the common case of a single store.
3511
3512 simple: {
3513 if (stores.len != 1) break :simple;
3514 const store_inst = sema.air_instructions.get(@backingInt(stores[0]));
3515 switch (store_inst.tag) {
3516 .store, .store_safe => {},
3517 .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => break :simple, // there's OPV stuff going on!
3518 else => unreachable,
3519 }
3520 if (store_inst.data.bin_op.lhs != alloc) break :simple;
3521
3522 const val = store_inst.data.bin_op.rhs.toInterned().?;
3523 assert(zcu.intern_pool.typeOf(val) == elem_ty.toIntern());
3524 return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value);
3525 }
3526
3527 // The simple strategy failed: we must create a mutable comptime alloc and
3528 // perform all of the runtime store operations at comptime.
3529
3530 const ct_alloc = try sema.newComptimeAlloc(block, .unneeded, elem_ty, ptr_info.flags.alignment);
3531
3532 const alloc_ptr = try pt.intern(.{ .ptr = .{
3533 .ty = alloc_ty.toIntern(),
3534 .base_addr = .{ .comptime_alloc = ct_alloc },
3535 .byte_offset = 0,
3536 } });
3537
3538 // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the comptime alloc
3539 var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena);
3540 try ptr_mapping.ensureTotalCapacity(@intCast(stores.len));
3541 ptr_mapping.putAssumeCapacity(alloc_inst, alloc_ptr);
3542
3543 // Whilst constructing our mapping, we will also initialize optional and error union payloads when
3544 // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters.
3545 var to_map = try std.array_list.Managed(Air.Inst.Index).initCapacity(sema.arena, stores.len);
3546
3547 for (stores) |store_inst_idx| {
3548 const store_inst = sema.air_instructions.get(@backingInt(store_inst_idx));
3549 const ptr_to_map = switch (store_inst.tag) {
3550 .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to.
3551 .set_union_tag => store_inst.data.bin_op.lhs.toIndex().?, // Map the union pointer.
3552 .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself.
3553 else => unreachable,
3554 };
3555 to_map.appendAssumeCapacity(ptr_to_map);
3556 }
3557
3558 const tmp_air = sema.getTmpAir();
3559
3560 while (to_map.pop()) |air_ptr| {
3561 if (ptr_mapping.contains(air_ptr)) continue;
3562 const PointerMethod = union(enum) {
3563 same_addr,
3564 opt_payload,
3565 eu_payload,
3566 field: u32,
3567 elem: u64,
3568 };
3569 const inst_tag = tmp_air.instructions.items(.tag)[@backingInt(air_ptr)];
3570 const air_parent_ptr: Air.Inst.Ref, const method: PointerMethod = switch (inst_tag) {
3571 .struct_field_ptr => blk: {
3572 const data = tmp_air.extraData(
3573 Air.StructField,
3574 tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_pl.payload,
3575 ).data;
3576 break :blk .{
3577 data.struct_operand,
3578 .{ .field = data.field_index },
3579 };
3580 },
3581 .struct_field_ptr_index_0,
3582 .struct_field_ptr_index_1,
3583 .struct_field_ptr_index_2,
3584 .struct_field_ptr_index_3,
3585 => .{
3586 tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand,
3587 .{ .field = switch (inst_tag) {
3588 .struct_field_ptr_index_0 => 0,
3589 .struct_field_ptr_index_1 => 1,
3590 .struct_field_ptr_index_2 => 2,
3591 .struct_field_ptr_index_3 => 3,
3592 else => unreachable,
3593 } },
3594 },
3595 .ptr_slice_ptr_ptr => .{
3596 tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand,
3597 .{ .field = Value.slice_ptr_index },
3598 },
3599 .ptr_slice_len_ptr => .{
3600 tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand,
3601 .{ .field = Value.slice_len_index },
3602 },
3603 .ptr_elem_ptr => blk: {
3604 const data = tmp_air.extraData(
3605 Air.Bin,
3606 tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_pl.payload,
3607 ).data;
3608 const idx_val = sema.resolveValue(data.rhs).?;
3609 break :blk .{
3610 data.lhs,
3611 .{ .elem = idx_val.toUnsignedInt(zcu) },
3612 };
3613 },
3614 .ptr_cast => .{
3615 tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand,
3616 .same_addr,
3617 },
3618 .optional_payload_ptr_set => .{
3619 tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand,
3620 .opt_payload,
3621 },
3622 .errunion_payload_ptr_set => .{
3623 tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand,
3624 .eu_payload,
3625 },
3626 else => unreachable,
3627 };
3628
3629 const decl_parent_ptr = ptr_mapping.get(air_parent_ptr.toIndex().?) orelse {
3630 // Resolve the parent pointer first.
3631 // Note that we add in what seems like the wrong order, because we're popping from the end of this array.
3632 try to_map.appendSlice(&.{ air_ptr, air_parent_ptr.toIndex().? });
3633 continue;
3634 };
3635 const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &zcu.intern_pool).toIntern();
3636 const new_ptr = switch (method) {
3637 .same_addr => try zcu.intern_pool.getCoerced(gpa, io, pt.tid, decl_parent_ptr, new_ptr_ty),
3638 .opt_payload => ptr: {
3639 // Set the optional to non-null at comptime.
3640 // If the payload is OPV, we must use that value instead of undef.
3641 const opt_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
3642 const payload_ty = opt_ty.optionalChild(zcu);
3643 const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty);
3644 const opt_val = try pt.intern(.{ .opt = .{
3645 .ty = opt_ty.toIntern(),
3646 .val = payload_val.toIntern(),
3647 } });
3648 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(opt_val), opt_ty);
3649 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrOptPayload(pt)).toIntern();
3650 },
3651 .eu_payload => ptr: {
3652 // Set the error union to non-error at comptime.
3653 // If the payload is OPV, we must use that value instead of undef.
3654 const eu_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
3655 const payload_ty = eu_ty.errorUnionPayload(zcu);
3656 const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty);
3657 const eu_val = try pt.intern(.{ .error_union = .{
3658 .ty = eu_ty.toIntern(),
3659 .val = .{ .payload = payload_val.toIntern() },
3660 } });
3661 try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(eu_val), eu_ty);
3662 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrEuPayload(pt)).toIntern();
3663 },
3664 .field => |idx| ptr: {
3665 const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu);
3666 if (zcu.typeToUnion(maybe_union_ty)) |union_obj| if (union_obj.layout == .auto) {
3667 // As this is a union field, we must store to the pointer now to set the tag.
3668 // The payload value will be stored later, so undef is a sufficent payload for now.
3669 const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]);
3670 const payload_val = try pt.undefValue(payload_ty);
3671 const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), idx);
3672 const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val);
3673 try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty);
3674 };
3675 break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern();
3676 },
3677 .elem => |idx| ptr: {
3678 const parent_ptr_val: Value = .fromInterned(decl_parent_ptr);
3679 if (parent_ptr_val.typeOf(zcu).childType(zcu).zigTypeTag(zcu) == .vector) {
3680 const elem_ptr_ty: Type = .fromInterned(new_ptr_ty);
3681 // Vectors are a bit weird; see logic in `elemPtrVector`.
3682 if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) {
3683 break :ptr (try pt.getCoerced(parent_ptr_val, elem_ptr_ty)).toIntern();
3684 } else {
3685 const bit_offset = idx * @divExact(elem_ptr_ty.childType(zcu).bitSize(zcu), 8);
3686 break :ptr (try parent_ptr_val.getOffsetPtr(bit_offset, elem_ptr_ty, pt)).toIntern();
3687 }
3688 }
3689 break :ptr (try parent_ptr_val.ptrElem(idx, pt)).toIntern();
3690 },
3691 };
3692 try ptr_mapping.put(air_ptr, new_ptr);
3693 }
3694
3695 // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime.
3696 // Any implicit stores performed by `optional_payload_ptr_set` or `errunion_payload_ptr_set`
3697 // instructions were already done above.
3698
3699 for (stores) |store_inst_idx| {
3700 const store_inst = sema.air_instructions.get(@backingInt(store_inst_idx));
3701 switch (store_inst.tag) {
3702 .optional_payload_ptr_set, .errunion_payload_ptr_set => {}, // Handled explicitly above
3703 .set_union_tag => {
3704 // Usually, we can ignore these, because the creation of the field pointer above
3705 // already did it for us. However, if the field is OPV, this is relevant, because
3706 // there is not going to be a store to the field. So we must initialize the union
3707 // tag if the field is OPV.
3708 const union_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
3709 const union_ptr_val: Value = .fromInterned(ptr_mapping.get(union_ptr_inst).?);
3710 const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?);
3711 const union_ty = union_ptr_val.typeOf(zcu).childType(zcu);
3712 const field_ty = union_ty.unionFieldType(tag_val, zcu).?;
3713 if (try field_ty.onePossibleValue(pt)) |payload_val| {
3714 const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val);
3715 try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty);
3716 }
3717 },
3718 .store, .store_safe => {
3719 const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?;
3720 const store_val = sema.resolveValue(store_inst.data.bin_op.rhs).?;
3721 const new_ptr = ptr_mapping.get(air_ptr_inst).?;
3722 try sema.storePtrVal(block, .unneeded, .fromInterned(new_ptr), store_val, store_val.typeOf(zcu));
3723 },
3724 else => unreachable,
3725 }
3726 }
3727
3728 // The value is finalized - load it!
3729 const val = (try sema.pointerDeref(block, LazySrcLoc.unneeded, Value.fromInterned(alloc_ptr), alloc_ty)).?.toIntern();
3730 return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, ct_alloc, alloc_inst, comptime_info.value);
3731}
3732
3733/// Given the resolved comptime-known value, rewrites the dead AIR to not
3734/// create a runtime stack allocation. Also places the resulting value into
3735/// either an anon decl ref or a comptime alloc depending on whether it
3736/// references comptime-mutable memory. If `existing_comptime_alloc` is
3737/// passed, it is a scratch allocation which already contains `result_val`.
3738/// Same return type as `resolveComptimeKnownAllocPtr` so we can tail call.
3739fn finishResolveComptimeKnownAllocPtr(
3740 sema: *Sema,
3741 block: *Block,
3742 alloc_ty: Type,
3743 result_val: InternPool.Index,
3744 existing_comptime_alloc: ?ComptimeAllocIndex,
3745 alloc_inst: Air.Inst.Index,
3746 comptime_info: MaybeComptimeAlloc,
3747) CompileError!?InternPool.Index {
3748 const pt = sema.pt;
3749 const zcu = pt.zcu;
3750
3751 // We're almost done - we have the resolved comptime value. We just need to
3752 // eliminate the now-dead runtime instructions.
3753
3754 // This instruction has type `alloc_ty`, meaning we can rewrite the `alloc` AIR instruction to
3755 // this one to drop the side effect. We also need to rewrite the stores; we'll turn them to this
3756 // too because it doesn't really matter what they become.
3757 const nop_inst: Air.Inst = .{ .tag = .ptr_from_int, .data = .{ .ty_op = .{
3758 .ty = alloc_ty,
3759 .operand = .zero_usize,
3760 } } };
3761
3762 sema.air_instructions.set(@backingInt(alloc_inst), nop_inst);
3763 for (comptime_info.stores.items(.inst)) |store_inst| {
3764 sema.air_instructions.set(@backingInt(store_inst), nop_inst);
3765 }
3766
3767 if (Value.fromInterned(result_val).canMutateComptimeVarState(zcu)) {
3768 const alloc_index = existing_comptime_alloc orelse a: {
3769 const idx = try sema.newComptimeAlloc(block, .unneeded, alloc_ty.childType(zcu), alloc_ty.ptrAlignment(zcu));
3770 const alloc = sema.getComptimeAlloc(idx);
3771 alloc.val = .{ .interned = result_val };
3772 break :a idx;
3773 };
3774 sema.getComptimeAlloc(alloc_index).is_const = true;
3775 return try pt.intern(.{ .ptr = .{
3776 .ty = alloc_ty.toIntern(),
3777 .base_addr = .{ .comptime_alloc = alloc_index },
3778 .byte_offset = 0,
3779 } });
3780 } else {
3781 return try pt.intern(.{ .ptr = .{
3782 .ty = alloc_ty.toIntern(),
3783 .base_addr = .{ .uav = .{
3784 .orig_ty = alloc_ty.toIntern(),
3785 .val = result_val,
3786 } },
3787 .byte_offset = 0,
3788 } });
3789 }
3790}
3791
3792fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type {
3793 var ptr_info = ptr_ty.ptrInfo(sema.pt.zcu);
3794 ptr_info.flags.is_const = true;
3795 return sema.pt.ptrType(ptr_info);
3796}
3797
3798fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref {
3799 const alloc_ty = sema.typeOf(alloc);
3800 const const_ptr_ty = try sema.makePtrTyConst(alloc_ty);
3801
3802 // Detect if a comptime value simply needs to have its type changed.
3803 if (sema.resolveValue(alloc)) |val| {
3804 return Air.internedToRef((try sema.pt.getCoerced(val, const_ptr_ty)).toIntern());
3805 }
3806
3807 return block.addTyOp(.ptr_cast, const_ptr_ty, alloc);
3808}
3809
3810fn zirAllocInferredComptime(
3811 sema: *Sema,
3812 is_const: bool,
3813) CompileError!Air.Inst.Ref {
3814 const gpa = sema.gpa;
3815
3816 try sema.air_instructions.append(gpa, .{
3817 .tag = .inferred_alloc_comptime,
3818 .data = .{ .inferred_alloc_comptime = .{
3819 .alignment = .none,
3820 .is_const = is_const,
3821 .ptr = undefined,
3822 } },
3823 });
3824 return @as(Air.Inst.Index, @fromBackingInt(@intCast(sema.air_instructions.len - 1))).toRef();
3825}
3826
3827fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3828 const pt = sema.pt;
3829 const zcu = pt.zcu;
3830
3831 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3832 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
3833 const var_src = block.nodeOffset(inst_data.src_node);
3834
3835 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
3836 try sema.ensureLayoutResolved(var_ty, var_src, .constant);
3837 if (block.isComptime() or var_ty.comptimeOnly(zcu)) {
3838 return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
3839 }
3840 if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) {
3841 const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
3842 return sema.fail(block, mut_src, "local variable in naked function", .{});
3843 }
3844 const target = zcu.getTarget();
3845 const ptr_type = try pt.ptrType(.{
3846 .child = var_ty.toIntern(),
3847 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
3848 });
3849 const ptr = try block.addTy(.alloc, ptr_type);
3850 const ptr_inst = ptr.toIndex().?;
3851 try sema.maybe_comptime_allocs.put(sema.gpa, ptr_inst, .{ .runtime_index = block.runtime_index });
3852 try sema.base_allocs.put(sema.gpa, ptr_inst, ptr_inst);
3853 return ptr;
3854}
3855
3856fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3857 const pt = sema.pt;
3858 const zcu = pt.zcu;
3859
3860 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3861 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
3862 const var_src = block.nodeOffset(inst_data.src_node);
3863
3864 const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
3865 try sema.ensureLayoutResolved(var_ty, var_src, .variable);
3866 if (block.isComptime()) {
3867 return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none);
3868 }
3869 if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) {
3870 const store_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
3871 return sema.fail(block, store_src, "local variable in naked function", .{});
3872 }
3873 try sema.validateVarType(block, ty_src, var_ty, false);
3874 const target = zcu.getTarget();
3875 const ptr_type = try pt.ptrType(.{
3876 .child = var_ty.toIntern(),
3877 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
3878 });
3879 return block.addTy(.alloc, ptr_type);
3880}
3881
3882fn zirAllocInferred(
3883 sema: *Sema,
3884 block: *Block,
3885 is_const: bool,
3886) CompileError!Air.Inst.Ref {
3887 const gpa = sema.gpa;
3888
3889 if (block.isComptime()) {
3890 try sema.air_instructions.append(gpa, .{
3891 .tag = .inferred_alloc_comptime,
3892 .data = .{ .inferred_alloc_comptime = .{
3893 .alignment = .none,
3894 .is_const = is_const,
3895 .ptr = undefined,
3896 } },
3897 });
3898 return @as(Air.Inst.Index, @fromBackingInt(@intCast(sema.air_instructions.len - 1))).toRef();
3899 }
3900
3901 const result_index = try block.addInstAsIndex(.{
3902 .tag = .inferred_alloc,
3903 .data = .{ .inferred_alloc = .{
3904 .alignment = .none,
3905 .is_const = is_const,
3906 } },
3907 });
3908 try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{});
3909 if (is_const) {
3910 try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index });
3911 try sema.base_allocs.put(sema.gpa, result_index, result_index);
3912 }
3913 return result_index.toRef();
3914}
3915
3916fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
3917 const pt = sema.pt;
3918 const zcu = pt.zcu;
3919 const gpa = sema.gpa;
3920 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
3921 const src = block.nodeOffset(inst_data.src_node);
3922 const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node });
3923 const ptr = sema.resolveInst(inst_data.operand);
3924 const ptr_inst = ptr.toIndex().?;
3925 const target = zcu.getTarget();
3926
3927 switch (sema.air_instructions.items(.tag)[@backingInt(ptr_inst)]) {
3928 .inferred_alloc_comptime => {
3929 // The work was already done for us by `Sema.storeToInferredAllocComptime`. Also, since
3930 // we had a value of the exact correct type to store, the result type's layout must be
3931 // already resolved. So all we need to do here is return the pointer.
3932 const iac = sema.air_instructions.items(.data)[@backingInt(ptr_inst)].inferred_alloc_comptime;
3933 const resolved_ptr = iac.ptr;
3934
3935 if (std.debug.runtime_safety) {
3936 // The inferred_alloc_comptime should never be referenced again
3937 sema.air_instructions.set(@backingInt(ptr_inst), .{ .tag = undefined, .data = undefined });
3938 }
3939
3940 const val = switch (zcu.intern_pool.indexToKey(resolved_ptr).ptr.base_addr) {
3941 .uav => |a| a.val,
3942 .comptime_alloc => |i| val: {
3943 const alloc = sema.getComptimeAlloc(i);
3944 break :val (try alloc.val.intern(pt, sema.arena)).toIntern();
3945 },
3946 else => unreachable,
3947 };
3948 if (zcu.intern_pool.isFuncBody(val)) {
3949 const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));
3950 if (ty.fnHasRuntimeBits(zcu)) {
3951 const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(val);
3952 try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index }));
3953 try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
3954 }
3955 }
3956
3957 return Air.internedToRef(resolved_ptr);
3958 },
3959 .inferred_alloc => {
3960 const ia1 = sema.air_instructions.items(.data)[@backingInt(ptr_inst)].inferred_alloc;
3961 const ia2 = sema.unresolved_inferred_allocs.fetchSwapRemove(ptr_inst).?.value;
3962 const peer_vals = try sema.arena.alloc(Air.Inst.Ref, ia2.prongs.items.len);
3963 for (peer_vals, ia2.prongs.items) |*peer_val, store_inst| {
3964 assert(sema.air_instructions.items(.tag)[@backingInt(store_inst)] == .store);
3965 const bin_op = sema.air_instructions.items(.data)[@backingInt(store_inst)].bin_op;
3966 peer_val.* = bin_op.rhs;
3967 }
3968 const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none);
3969 // The layout of the peers is already resolved, so the layout of `final_elem_ty` is too.
3970 final_elem_ty.assertHasLayout(zcu);
3971
3972 const final_ptr_ty = try pt.ptrType(.{
3973 .child = final_elem_ty.toIntern(),
3974 .flags = .{
3975 .alignment = ia1.alignment,
3976 .address_space = target_util.defaultAddressSpace(target, .local),
3977 },
3978 });
3979
3980 if (!ia1.is_const) {
3981 try sema.validateVarType(block, ty_src, final_elem_ty, false);
3982 } else if (try sema.resolveComptimeKnownAllocPtr(block, ptr, final_ptr_ty)) |ptr_val| {
3983 const const_ptr_ty = try sema.makePtrTyConst(final_ptr_ty);
3984 const new_const_ptr = try pt.getCoerced(Value.fromInterned(ptr_val), const_ptr_ty);
3985
3986 return Air.internedToRef(new_const_ptr.toIntern());
3987 }
3988
3989 if (final_elem_ty.comptimeOnly(zcu)) {
3990 // The alloc wasn't comptime-known per the above logic, so the
3991 // type cannot be comptime-only.
3992 // TODO: source location of runtime control flow
3993 return sema.fail(block, src, "value with comptime-only type '{f}' depends on runtime control flow", .{final_elem_ty.fmt(pt)});
3994 }
3995 if (sema.func_is_naked and final_elem_ty.hasRuntimeBits(zcu)) {
3996 const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
3997 return sema.fail(block, mut_src, "local variable in naked function", .{});
3998 }
3999 // Change it to a normal alloc.
4000 sema.air_instructions.set(@backingInt(ptr_inst), .{
4001 .tag = .alloc,
4002 .data = .{ .ty = final_ptr_ty },
4003 });
4004
4005 // Now we need to go back over all the store instructions, and do the logic as if
4006 // the new result ptr type was available.
4007
4008 for (ia2.prongs.items) |placeholder_inst| {
4009 var replacement_block = block.makeSubBlock();
4010 defer replacement_block.instructions.deinit(gpa);
4011
4012 assert(sema.air_instructions.items(.tag)[@backingInt(placeholder_inst)] == .store);
4013 const bin_op = sema.air_instructions.items(.data)[@backingInt(placeholder_inst)].bin_op;
4014 try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .store);
4015
4016 // If only one instruction is produced then we can replace the store
4017 // placeholder instruction with this instruction; no need for an entire block.
4018 if (replacement_block.instructions.items.len == 1) {
4019 const only_inst = replacement_block.instructions.items[0];
4020 sema.air_instructions.set(@backingInt(placeholder_inst), sema.air_instructions.get(@backingInt(only_inst)));
4021 continue;
4022 }
4023
4024 // Here we replace the placeholder store instruction with a block
4025 // that does the actual store logic.
4026 _ = try replacement_block.addBr(placeholder_inst, .void_value);
4027 try sema.air_extra.ensureUnusedCapacity(
4028 gpa,
4029 @typeInfo(Air.Block).@"struct".field_names.len + replacement_block.instructions.items.len,
4030 );
4031 sema.air_instructions.set(@backingInt(placeholder_inst), .{
4032 .tag = .block,
4033 .data = .{ .ty_pl = .{
4034 .ty = .void,
4035 .payload = sema.addExtraAssumeCapacity(Air.Block{
4036 .body_len = @intCast(replacement_block.instructions.items.len),
4037 }),
4038 } },
4039 });
4040 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
4041 }
4042
4043 if (ia1.is_const) {
4044 return sema.makePtrConst(block, ptr);
4045 } else {
4046 return ptr;
4047 }
4048 },
4049 else => unreachable,
4050 }
4051}
4052
4053fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4054 const pt = sema.pt;
4055 const zcu = pt.zcu;
4056 const comp = zcu.comp;
4057 const gpa = comp.gpa;
4058 const io = comp.io;
4059 const ip = &zcu.intern_pool;
4060
4061 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
4062 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
4063 const all_args = sema.code.refSlice(extra.end, extra.data.operands_len);
4064 const arg_pairs: []const [2]Zir.Inst.Ref = @as([*]const [2]Zir.Inst.Ref, @ptrCast(all_args))[0..@divExact(all_args.len, 2)];
4065 const src = block.nodeOffset(inst_data.src_node);
4066
4067 var len: Air.Inst.Ref = .none;
4068 var len_val: ?Value = null;
4069 var len_idx: u32 = undefined;
4070 var any_runtime = false;
4071
4072 const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, arg_pairs.len);
4073 defer gpa.free(runtime_arg_lens);
4074
4075 // First pass to look for comptime values.
4076 for (arg_pairs, 0..) |zir_arg_pair, i_usize| {
4077 const i: u32 = @intCast(i_usize);
4078 runtime_arg_lens[i] = .none;
4079 if (zir_arg_pair[0] == .none) continue;
4080
4081 const arg_src = block.src(.{ .for_input = .{
4082 .for_node_offset = inst_data.src_node,
4083 .input_index = i,
4084 } });
4085
4086 const arg_len_uncoerced = if (zir_arg_pair[1] == .none) l: {
4087 // This argument is an indexable.
4088 const object = sema.resolveInst(zir_arg_pair[0]);
4089 const object_ty = sema.typeOf(object);
4090 if (!object_ty.isIndexable(zcu)) {
4091 // Instead of using checkIndexable we customize this error.
4092 const msg = msg: {
4093 const msg = try sema.errMsg(arg_src, "type '{f}' is not indexable and not a range", .{object_ty.fmt(pt)});
4094 errdefer msg.destroy(sema.gpa);
4095 try sema.errNote(arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{});
4096
4097 if (object_ty.zigTypeTag(zcu) == .error_union) {
4098 try sema.errNote(arg_src, msg, "consider using 'try', 'catch', or 'if'", .{});
4099 }
4100
4101 break :msg msg;
4102 };
4103 return sema.failWithOwnedErrorMsg(block, msg);
4104 }
4105 if (!object_ty.indexableHasLen(zcu)) continue;
4106 break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), arg_src);
4107 } else l: {
4108 // This argument is a range.
4109 const range_start = sema.resolveInst(zir_arg_pair[0]);
4110 const range_end = sema.resolveInst(zir_arg_pair[1]);
4111 if (try sema.resolveDefinedValue(block, arg_src, range_start)) |start| {
4112 if (try sema.valuesEqual(start, .zero_usize, .usize)) break :l range_end;
4113 }
4114 break :l try sema.analyzeArithmetic(block, .sub, range_end, range_start, arg_src, arg_src, arg_src, true);
4115 };
4116 const arg_len = try sema.coerce(block, .usize, arg_len_uncoerced, arg_src);
4117 if (len == .none) {
4118 len = arg_len;
4119 len_idx = i;
4120 }
4121 if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| {
4122 if (len_val) |v| {
4123 if (!(try sema.valuesEqual(arg_val, v, .usize))) {
4124 const msg = msg: {
4125 const msg = try sema.errMsg(src, "non-matching for loop lengths", .{});
4126 errdefer msg.destroy(gpa);
4127 const a_src = block.src(.{ .for_input = .{
4128 .for_node_offset = inst_data.src_node,
4129 .input_index = len_idx,
4130 } });
4131 try sema.errNote(a_src, msg, "length {f} here", .{
4132 v.fmtValueSema(pt, sema),
4133 });
4134 try sema.errNote(arg_src, msg, "length {f} here", .{
4135 arg_val.fmtValueSema(pt, sema),
4136 });
4137 break :msg msg;
4138 };
4139 return sema.failWithOwnedErrorMsg(block, msg);
4140 }
4141 } else {
4142 len = arg_len;
4143 len_val = arg_val;
4144 len_idx = i;
4145 }
4146 continue;
4147 }
4148 runtime_arg_lens[i] = arg_len;
4149 any_runtime = true;
4150 }
4151
4152 if (len == .none) {
4153 const msg = msg: {
4154 const msg = try sema.errMsg(src, "unbounded for loop", .{});
4155 errdefer msg.destroy(gpa);
4156 for (arg_pairs, 0..) |zir_arg_pair, i_usize| {
4157 const i: u32 = @intCast(i_usize);
4158 if (zir_arg_pair[0] == .none) continue;
4159 if (zir_arg_pair[1] != .none) continue;
4160 const object = sema.resolveInst(zir_arg_pair[0]);
4161 const object_ty = sema.typeOf(object);
4162 const arg_src = block.src(.{ .for_input = .{
4163 .for_node_offset = inst_data.src_node,
4164 .input_index = i,
4165 } });
4166 try sema.errNote(arg_src, msg, "type '{f}' has no upper bound", .{
4167 object_ty.fmt(pt),
4168 });
4169 }
4170 break :msg msg;
4171 };
4172 return sema.failWithOwnedErrorMsg(block, msg);
4173 }
4174
4175 // Now for the runtime checks.
4176 if (any_runtime and block.wantSafety()) {
4177 var ok: Air.Inst.Ref = .none;
4178 for (runtime_arg_lens, 0..) |arg_len, i| {
4179 if (arg_len == .none) continue;
4180 if (i == len_idx) continue;
4181 const eq = try block.addBinOp(.cmp_eq, len, arg_len);
4182 ok = if (ok != .none)
4183 try block.addBinOp(.bit_and, ok, eq)
4184 else
4185 eq;
4186 }
4187 if (ok != .none)
4188 try sema.addSafetyCheck(block, src, ok, .for_len_mismatch);
4189 }
4190
4191 return len;
4192}
4193
4194/// Given any single pointer, retrieve a pointer to the payload of any optional
4195/// or error union pointed to, initializing these pointers along the way.
4196/// Given a `*E!?T`, returns a (valid) `*T`.
4197/// May invalidate already-stored payload data.
4198/// Asserts that the layout of the pointer child type is already resolved.
4199fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref {
4200 const pt = sema.pt;
4201 const zcu = pt.zcu;
4202 sema.typeOf(ptr).childType(zcu).assertHasLayout(zcu);
4203 var base_ptr = ptr;
4204 while (true) switch (sema.typeOf(base_ptr).childType(zcu).zigTypeTag(zcu)) {
4205 .error_union => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true),
4206 .optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true),
4207 else => break,
4208 };
4209 try sema.checkKnownAllocPtr(block, ptr, base_ptr);
4210 return base_ptr;
4211}
4212
4213fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4214 const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
4215 const ptr = sema.resolveInst(un_node.operand);
4216 const src = block.nodeOffset(un_node.src_node);
4217 try sema.ensureLayoutResolved(sema.typeOf(ptr).childType(sema.pt.zcu), src, .init);
4218 return sema.optEuBasePtrInit(block, ptr, src);
4219}
4220
4221fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4222 const pt = sema.pt;
4223 const zcu = pt.zcu;
4224 const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
4225 const src = block.nodeOffset(pl_node.src_node);
4226 const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data;
4227 const uncoerced_val = sema.resolveInst(extra.rhs);
4228 const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.lhs) orelse return uncoerced_val;
4229 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu);
4230 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
4231 const elem_ty = ptr_ty.childType(zcu);
4232 switch (ptr_ty.ptrSize(zcu)) {
4233 .one => {
4234 const uncoerced_ty = sema.typeOf(uncoerced_val);
4235 if (elem_ty.zigTypeTag(zcu) == .array and elem_ty.childType(zcu).toIntern() == uncoerced_ty.toIntern()) {
4236 // We're trying to initialize a *[1]T with a reference to a T - don't perform any coercion.
4237 return uncoerced_val;
4238 }
4239 // If the destination type is anyopaque, don't coerce - the pointer will coerce instead.
4240 if (elem_ty.toIntern() == .anyopaque_type) {
4241 return uncoerced_val;
4242 } else {
4243 return sema.coerce(block, elem_ty, uncoerced_val, src);
4244 }
4245 },
4246 .slice, .many => {
4247 // Our goal is to coerce `uncoerced_val` to an array of `elem_ty`.
4248 const val_ty = sema.typeOf(uncoerced_val);
4249 switch (val_ty.zigTypeTag(zcu)) {
4250 .array, .vector => {},
4251 else => if (!val_ty.isTuple(zcu)) {
4252 return sema.fail(block, src, "expected array of '{f}', found '{f}'", .{ elem_ty.fmt(pt), val_ty.fmt(pt) });
4253 },
4254 }
4255 const want_ty = try pt.arrayType(.{
4256 .len = val_ty.arrayLen(zcu),
4257 .child = elem_ty.toIntern(),
4258 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
4259 });
4260 return sema.coerce(block, want_ty, uncoerced_val, src);
4261 },
4262 .c => {
4263 // There's nothing meaningful to do here, because we don't know if this is meant to be a
4264 // single-pointer or a many-pointer.
4265 return uncoerced_val;
4266 },
4267 }
4268}
4269
4270fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref {
4271 const pt = sema.pt;
4272 const zcu = pt.zcu;
4273 const comp = zcu.comp;
4274 const gpa = comp.gpa;
4275 const io = comp.io;
4276
4277 const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
4278 const src = block.nodeOffset(un_node.src_node);
4279
4280 const operand_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type;
4281
4282 const payload_ty = if (is_ref) ty: {
4283 if (!operand_ty.isSinglePointer(zcu)) {
4284 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`.
4285 }
4286 break :ty operand_ty.childType(zcu);
4287 } else operand_ty;
4288
4289 const err_set_ty: Type = err_set: {
4290 // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals
4291 // until we hit an error union or set.
4292 var cur_ty = sema.fn_ret_ty;
4293 while (true) {
4294 switch (cur_ty.zigTypeTag(zcu)) {
4295 .error_set => break :err_set cur_ty,
4296 .error_union => break :err_set cur_ty.errorUnionSet(zcu),
4297 .optional => cur_ty = cur_ty.optionalChild(zcu),
4298 else => {
4299 // This function cannot return an error.
4300 // `try` is still valid if the error case is impossible, i.e. no error is returned.
4301 // So, the result type has an error set of `error{}`.
4302 break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(gpa, io, pt.tid, &.{}));
4303 },
4304 }
4305 }
4306 };
4307
4308 const eu_ty = try pt.errorUnionType(err_set_ty, payload_ty);
4309
4310 if (is_ref) {
4311 var ptr_info = operand_ty.ptrInfo(zcu);
4312 ptr_info.child = eu_ty.toIntern();
4313 const eu_ptr_ty = try pt.ptrType(ptr_info);
4314 return Air.internedToRef(eu_ptr_ty.toIntern());
4315 } else {
4316 return Air.internedToRef(eu_ty.toIntern());
4317 }
4318}
4319
4320fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
4321 const pt = sema.pt;
4322 const zcu = pt.zcu;
4323 const un_tok = sema.code.instructions.items(.data)[@backingInt(inst)].un_tok;
4324 const src = block.tokenOffset(un_tok.src_tok);
4325 // In case of GenericPoison, we don't actually have a type, so this will be
4326 // treated as an untyped address-of operator.
4327 const ty_operand = try sema.resolveTypeOrPoison(block, src, un_tok.operand) orelse return;
4328 if (ty_operand.optEuBaseType(zcu).zigTypeTag(zcu) != .pointer) {
4329 return sema.failWithOwnedErrorMsg(block, msg: {
4330 const msg = try sema.errMsg(src, "expected type '{f}', found pointer", .{ty_operand.fmt(pt)});
4331 errdefer msg.destroy(sema.gpa);
4332 try sema.errNote(src, msg, "address-of operator always returns a pointer", .{});
4333 break :msg msg;
4334 });
4335 }
4336}
4337
4338fn zirValidateConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
4339 if (!block.isComptime()) return;
4340
4341 const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
4342 const src = block.nodeOffset(un_node.src_node);
4343 const init_ref = sema.resolveInst(un_node.operand);
4344 if (!try sema.isComptimeKnown(init_ref)) {
4345 return sema.failWithNeededComptime(block, src, null);
4346 }
4347}
4348
4349fn zirValidateArrayInitRefTy(
4350 sema: *Sema,
4351 block: *Block,
4352 inst: Zir.Inst.Index,
4353) CompileError!Air.Inst.Ref {
4354 const pt = sema.pt;
4355 const zcu = pt.zcu;
4356 const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
4357 const src = block.nodeOffset(pl_node.src_node);
4358 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
4359 const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.ptr_ty) orelse return .generic_poison_type;
4360 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu);
4361 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
4362 switch (zcu.intern_pool.indexToKey(ptr_ty.toIntern())) {
4363 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
4364 .slice, .many => {
4365 // Use array of correct length
4366 const arr_ty = try pt.arrayType(.{
4367 .len = extra.elem_count,
4368 .child = ptr_ty.childType(zcu).toIntern(),
4369 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
4370 });
4371 return Air.internedToRef(arr_ty.toIntern());
4372 },
4373 else => {},
4374 },
4375 else => {},
4376 }
4377 // Otherwise, we just want the pointer child type
4378 const ret_ty = ptr_ty.childType(zcu);
4379 if (ret_ty.toIntern() == .anyopaque_type) {
4380 // The actual array type is unknown, which we represent with a generic poison.
4381 return .generic_poison_type;
4382 }
4383 const arr_ty = ret_ty.optEuBaseType(zcu);
4384 try sema.validateArrayInitTy(block, src, src, extra.elem_count, arr_ty);
4385 return Air.internedToRef(ret_ty.toIntern());
4386}
4387
4388fn zirValidateArrayInitTy(
4389 sema: *Sema,
4390 block: *Block,
4391 inst: Zir.Inst.Index,
4392 is_result_ty: bool,
4393) CompileError!void {
4394 const pt = sema.pt;
4395 const zcu = pt.zcu;
4396 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
4397 const src = block.nodeOffset(inst_data.src_node);
4398 const ty_src: LazySrcLoc = if (is_result_ty) src else block.src(.{ .node_offset_init_ty = inst_data.src_node });
4399 const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
4400 // It's okay for the type to be poison: this will result in an anonymous array init.
4401 const ty = try sema.resolveTypeOrPoison(block, ty_src, extra.ty) orelse return;
4402 const arr_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty;
4403 return sema.validateArrayInitTy(block, src, ty_src, extra.init_count, arr_ty);
4404}
4405
4406fn validateArrayInitTy(
4407 sema: *Sema,
4408 block: *Block,
4409 src: LazySrcLoc,
4410 ty_src: LazySrcLoc,
4411 init_count: u32,
4412 ty: Type,
4413) CompileError!void {
4414 const pt = sema.pt;
4415 const zcu = pt.zcu;
4416 switch (ty.zigTypeTag(zcu)) {
4417 .array => {
4418 const array_len = ty.arrayLen(zcu);
4419 if (init_count != array_len) {
4420 return sema.fail(block, src, "expected {d} array elements; found {d}", .{
4421 array_len, init_count,
4422 });
4423 }
4424 return;
4425 },
4426 .vector => {
4427 const array_len = ty.arrayLen(zcu);
4428 if (init_count != array_len) {
4429 return sema.fail(block, src, "expected {d} vector elements; found {d}", .{
4430 array_len, init_count,
4431 });
4432 }
4433 return;
4434 },
4435 .@"struct" => if (ty.isTuple(zcu)) {
4436 const array_len = ty.arrayLen(zcu);
4437 if (init_count > array_len) {
4438 return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{
4439 array_len, init_count,
4440 });
4441 }
4442 return;
4443 },
4444 else => {},
4445 }
4446 return sema.failWithArrayInitNotSupported(block, ty_src, ty);
4447}
4448
4449fn zirValidateStructInitTy(
4450 sema: *Sema,
4451 block: *Block,
4452 inst: Zir.Inst.Index,
4453 is_result_ty: bool,
4454) CompileError!void {
4455 const pt = sema.pt;
4456 const zcu = pt.zcu;
4457 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
4458 const src = block.nodeOffset(inst_data.src_node);
4459 // It's okay for the type to be poison: this will result in an anonymous struct init.
4460 const ty = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse return;
4461 const struct_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty;
4462
4463 switch (struct_ty.zigTypeTag(zcu)) {
4464 .@"struct", .@"union" => return,
4465 else => {},
4466 }
4467 return sema.failWithStructInitNotSupported(block, src, struct_ty);
4468}
4469
4470fn zirValidatePtrStructInit(
4471 sema: *Sema,
4472 block: *Block,
4473 inst: Zir.Inst.Index,
4474) CompileError!void {
4475 const pt = sema.pt;
4476 const zcu = pt.zcu;
4477 const validate_inst = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
4478 const init_src = block.nodeOffset(validate_inst.src_node);
4479 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
4480 const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
4481 const field_ptr_data = sema.code.instructions.items(.data)[@backingInt(instrs[0])].pl_node;
4482 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
4483 const object_ptr = sema.resolveInst(field_ptr_extra.lhs);
4484 const agg_ty = sema.typeOf(object_ptr).childType(zcu).optEuBaseType(zcu);
4485 switch (agg_ty.zigTypeTag(zcu)) {
4486 .@"struct" => return sema.validateStructInit(
4487 block,
4488 agg_ty,
4489 init_src,
4490 instrs,
4491 object_ptr,
4492 ),
4493 .@"union" => return sema.validateUnionInit(
4494 block,
4495 agg_ty,
4496 init_src,
4497 instrs,
4498 ),
4499 else => unreachable,
4500 }
4501}
4502
4503fn validateUnionInit(
4504 sema: *Sema,
4505 block: *Block,
4506 union_ty: Type,
4507 init_src: LazySrcLoc,
4508 instrs: []const Zir.Inst.Index,
4509) CompileError!void {
4510 if (instrs.len == 1) {
4511 // Trvial validation done, and the union tag was already set by machinery in `unionFieldPtr`.
4512 return;
4513 }
4514 const msg = msg: {
4515 const msg = try sema.errMsg(
4516 init_src,
4517 "cannot initialize multiple union fields at once; unions can only have one active field",
4518 .{},
4519 );
4520 errdefer msg.destroy(sema.gpa);
4521
4522 for (instrs[1..]) |inst| {
4523 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
4524 const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node });
4525 try sema.errNote(inst_src, msg, "additional initializer here", .{});
4526 }
4527 try sema.addDeclaredHereNote(msg, union_ty);
4528 break :msg msg;
4529 };
4530 return sema.failWithOwnedErrorMsg(block, msg);
4531}
4532
4533fn validateStructInit(
4534 sema: *Sema,
4535 block: *Block,
4536 struct_ty: Type,
4537 init_src: LazySrcLoc,
4538 instrs: []const Zir.Inst.Index,
4539 struct_ptr: Air.Inst.Ref,
4540) CompileError!void {
4541 const pt = sema.pt;
4542 const zcu = pt.zcu;
4543 const comp = zcu.comp;
4544 const gpa = comp.gpa;
4545 const io = comp.io;
4546 const ip = &zcu.intern_pool;
4547
4548 // Tracks whether each field was explicitly initialized.
4549 const found_fields = try gpa.alloc(bool, struct_ty.structFieldCount(zcu));
4550 defer gpa.free(found_fields);
4551 @memset(found_fields, false);
4552
4553 for (instrs) |field_ptr| {
4554 const field_ptr_data = sema.code.instructions.items(.data)[@backingInt(field_ptr)].pl_node;
4555 const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node });
4556 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
4557 const field_name = try ip.getOrPutString(
4558 gpa,
4559 io,
4560 pt.tid,
4561 sema.code.nullTerminatedString(field_ptr_extra.field_name_start),
4562 .no_embedded_nulls,
4563 );
4564 const field_index = if (struct_ty.isTuple(zcu))
4565 try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
4566 else
4567 try sema.structFieldIndex(block, struct_ty, field_name, field_src);
4568 assert(found_fields[field_index] == false);
4569 found_fields[field_index] = true;
4570 }
4571
4572 // Our job is simply to deal with default field values. Specifically, any field which was not
4573 // explicitly initialized must have its default value stored to the field pointer, or, if the
4574 // field has no default value, a compile error must be emitted instead.
4575
4576 // In the past, this code had other responsibilities, which involved some nasty AIR rewrites. However,
4577 // that work was actually all redundant:
4578 //
4579 // * If the struct value is comptime-known, field stores remain a perfectly valid way of initializing
4580 // the struct through RLS; there is no need to turn the field stores into one store. Comptime-known
4581 // consts are handled correctly either way thanks to `maybe_comptime_allocs` and friends.
4582 //
4583 // * If the struct type is comptime-only, we need to make sure all of the fields were comptime-known.
4584 // But the comptime-only type means that `struct_ptr` must be a comptime-mutable pointer, so the
4585 // field stores were to comptime-mutable pointers, so have already errored if not comptime-known.
4586 //
4587 // * If the value is runtime-known, then comptime-known fields must be validated as runtime values.
4588 // But this was already handled for every field store by the machinery in `checkComptimeKnownStore`.
4589
4590 var root_msg: ?*Zcu.ErrorMsg = null;
4591 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
4592
4593 for (found_fields, 0..) |explicit, i_usize| {
4594 const i: u32 = @intCast(i_usize);
4595
4596 if (explicit) continue;
4597 if (struct_ty.structFieldIsComptime(i, zcu)) continue;
4598
4599 if (!struct_ty.isTuple(zcu)) {
4600 try sema.ensureStructDefaultsResolved(struct_ty, init_src);
4601 }
4602
4603 const default_val = struct_ty.structFieldDefaultValue(i, zcu) orelse {
4604 const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse {
4605 const template = "missing tuple field with index {d}";
4606 if (root_msg) |msg| {
4607 try sema.errNote(init_src, msg, template, .{i});
4608 } else {
4609 root_msg = try sema.errMsg(init_src, template, .{i});
4610 }
4611 continue;
4612 };
4613 const template = "missing struct field: {f}";
4614 const args = .{field_name.fmt(ip)};
4615 if (root_msg) |msg| {
4616 try sema.errNote(init_src, msg, template, args);
4617 } else {
4618 root_msg = try sema.errMsg(init_src, template, args);
4619 }
4620 continue;
4621 };
4622
4623 const field_src = init_src; // TODO better source location
4624 const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty);
4625 try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr);
4626 try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store);
4627 }
4628
4629 if (root_msg) |msg| {
4630 try sema.addDeclaredHereNote(msg, struct_ty);
4631 root_msg = null;
4632 return sema.failWithOwnedErrorMsg(block, msg);
4633 }
4634}
4635
4636fn zirValidatePtrArrayInit(
4637 sema: *Sema,
4638 block: *Block,
4639 inst: Zir.Inst.Index,
4640) CompileError!void {
4641 const pt = sema.pt;
4642 const zcu = pt.zcu;
4643 const validate_inst = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
4644 const init_src = block.nodeOffset(validate_inst.src_node);
4645 const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
4646 const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
4647 const first_elem_ptr_data = sema.code.instructions.items(.data)[@backingInt(instrs[0])].pl_node;
4648 const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data;
4649 const array_ptr = sema.resolveInst(elem_ptr_extra.ptr);
4650 const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu);
4651 const array_len = array_ty.arrayLen(zcu);
4652
4653 // Analagously to `validateStructInit`, our job is to handle default fields; either emitting AIR
4654 // to initialize them, or emitting a compile error if an unspecified field has no default. For
4655 // tuples, there are literally default field values, although they're guaranteed to be comptime
4656 // fields so we don't need to initialize them. For arrays, we may have a sentinel, which is never
4657 // specified so we always need to initialize here. For vectors, there's no such thing.
4658
4659 switch (array_ty.zigTypeTag(zcu)) {
4660 .@"struct" => if (instrs.len != array_len) {
4661 var root_msg: ?*Zcu.ErrorMsg = null;
4662 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
4663
4664 var i = instrs.len;
4665 while (i < array_len) : (i += 1) {
4666 if (array_ty.structFieldDefaultValue(i, zcu) == null) {
4667 const template = "missing tuple field with index {d}";
4668 if (root_msg) |msg| {
4669 try sema.errNote(init_src, msg, template, .{i});
4670 } else {
4671 root_msg = try sema.errMsg(init_src, template, .{i});
4672 }
4673 continue;
4674 }
4675 }
4676
4677 if (root_msg) |msg| {
4678 root_msg = null;
4679 return sema.failWithOwnedErrorMsg(block, msg);
4680 }
4681 },
4682
4683 .array => if (instrs.len != array_len) {
4684 return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
4685 array_len, instrs.len,
4686 });
4687 } else if (array_ty.sentinel(zcu)) |sentinel| {
4688 const array_len_ref = try pt.intRef(.usize, array_len);
4689 const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true);
4690 try sema.checkKnownAllocPtr(block, array_ptr, sentinel_ptr);
4691 try sema.storePtr2(block, init_src, sentinel_ptr, init_src, .fromValue(sentinel), init_src, .store);
4692 },
4693
4694 .vector => if (instrs.len != array_len) {
4695 return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
4696 array_len, instrs.len,
4697 });
4698 },
4699
4700 else => unreachable,
4701 }
4702}
4703
4704fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
4705 const pt = sema.pt;
4706 const zcu = pt.zcu;
4707 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
4708 const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data;
4709 const src = block.nodeOffset(inst_data.src_node);
4710 const destructure_src = block.nodeOffset(extra.destructure_node);
4711 const operand = sema.resolveInst(extra.operand);
4712 const operand_ty = sema.typeOf(operand);
4713
4714 if (!operand_ty.destructurable(zcu)) {
4715 return sema.failWithOwnedErrorMsg(block, msg: {
4716 const msg = try sema.errMsg(src, "type '{f}' cannot be destructured", .{operand_ty.fmt(pt)});
4717 errdefer msg.destroy(sema.gpa);
4718 try sema.errNote(destructure_src, msg, "result destructured here", .{});
4719 if (operand_ty.zigTypeTag(pt.zcu) == .error_union) {
4720 const base_op_ty = operand_ty.errorUnionPayload(zcu);
4721 if (base_op_ty.destructurable(zcu))
4722 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
4723 }
4724 break :msg msg;
4725 });
4726 }
4727
4728 if (operand_ty.arrayLen(zcu) != extra.expect_len) {
4729 return sema.failWithOwnedErrorMsg(block, msg: {
4730 const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{
4731 extra.expect_len, operand_ty.arrayLen(zcu),
4732 });
4733 errdefer msg.destroy(sema.gpa);
4734 try sema.errNote(destructure_src, msg, "result destructured here", .{});
4735 break :msg msg;
4736 });
4737 }
4738}
4739
4740fn failWithBadMemberAccess(
4741 sema: *Sema,
4742 block: *Block,
4743 agg_ty: Type,
4744 field_src: LazySrcLoc,
4745 field_name: InternPool.NullTerminatedString,
4746) CompileError {
4747 const pt = sema.pt;
4748 const zcu = pt.zcu;
4749 const ip = &zcu.intern_pool;
4750 const kw_name = switch (agg_ty.zigTypeTag(zcu)) {
4751 .@"union" => "union",
4752 .@"struct" => "struct",
4753 .@"opaque" => "opaque",
4754 .@"enum" => "enum",
4755 else => unreachable,
4756 };
4757 if (agg_ty.typeDeclInst(zcu)) |inst| {
4758 const inst_index = inst.resolve(ip) orelse return sema.failTransitive(.{ .lost_tracking = inst });
4759 if (inst_index == .main_struct_inst) {
4760 return sema.fail(block, field_src, "root source file struct '{f}' has no member named '{f}'", .{
4761 agg_ty.fmt(pt), field_name.fmt(ip),
4762 });
4763 }
4764 }
4765
4766 return sema.fail(block, field_src, "{s} '{f}' has no member named '{f}'", .{
4767 kw_name, agg_ty.fmt(pt), field_name.fmt(ip),
4768 });
4769}
4770
4771fn failWithBadStructFieldAccess(
4772 sema: *Sema,
4773 block: *Block,
4774 struct_ty: Type,
4775 struct_type: InternPool.LoadedStructType,
4776 field_src: LazySrcLoc,
4777 field_name: InternPool.NullTerminatedString,
4778) CompileError {
4779 const pt = sema.pt;
4780 const zcu = pt.zcu;
4781 const ip = &zcu.intern_pool;
4782
4783 const msg = msg: {
4784 const msg = try sema.errMsg(
4785 field_src,
4786 "no field named '{f}' in struct '{f}'",
4787 .{ field_name.fmt(ip), struct_type.fqn.fmt(ip) },
4788 );
4789 errdefer msg.destroy(sema.gpa);
4790 try sema.errNote(struct_ty.srcLoc(zcu), msg, "struct declared here", .{});
4791 break :msg msg;
4792 };
4793 return sema.failWithOwnedErrorMsg(block, msg);
4794}
4795
4796fn failWithBadUnionFieldAccess(
4797 sema: *Sema,
4798 block: *Block,
4799 union_ty: Type,
4800 union_obj: InternPool.LoadedUnionType,
4801 field_src: LazySrcLoc,
4802 field_name: InternPool.NullTerminatedString,
4803) CompileError {
4804 const pt = sema.pt;
4805 const zcu = pt.zcu;
4806 const ip = &zcu.intern_pool;
4807 const gpa = sema.gpa;
4808
4809 const msg = msg: {
4810 const msg = try sema.errMsg(
4811 field_src,
4812 "no field named '{f}' in union '{f}'",
4813 .{ field_name.fmt(ip), union_obj.fqn.fmt(ip) },
4814 );
4815 errdefer msg.destroy(gpa);
4816 try sema.errNote(union_ty.srcLoc(zcu), msg, "union declared here", .{});
4817 break :msg msg;
4818 };
4819 return sema.failWithOwnedErrorMsg(block, msg);
4820}
4821
4822pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) Allocator.Error!void {
4823 const zcu = sema.pt.zcu;
4824 const src_loc = decl_ty.srcLocOrNull(zcu) orelse return;
4825 const category = switch (decl_ty.zigTypeTag(zcu)) {
4826 .@"union" => "union",
4827 .@"struct" => "struct",
4828 .@"enum" => "enum",
4829 .@"opaque" => "opaque",
4830 else => unreachable,
4831 };
4832 try sema.errNote(src_loc, parent, "{s} declared here", .{category});
4833}
4834
4835fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
4836 const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
4837 const src = block.nodeOffset(pl_node.src_node);
4838 const bin = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data;
4839 const ptr = sema.resolveInst(bin.lhs);
4840 const operand = sema.resolveInst(bin.rhs);
4841 const ptr_inst = ptr.toIndex().?;
4842 const air_datas = sema.air_instructions.items(.data);
4843
4844 switch (sema.air_instructions.items(.tag)[@backingInt(ptr_inst)]) {
4845 .inferred_alloc_comptime => {
4846 const iac = &air_datas[@backingInt(ptr_inst)].inferred_alloc_comptime;
4847 return sema.storeToInferredAllocComptime(block, src, operand, iac);
4848 },
4849 .inferred_alloc => {
4850 const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?;
4851 return sema.storeToInferredAlloc(block, src, ptr, operand, ia);
4852 },
4853 else => unreachable,
4854 }
4855}
4856
4857fn storeToInferredAlloc(
4858 sema: *Sema,
4859 block: *Block,
4860 src: LazySrcLoc,
4861 ptr: Air.Inst.Ref,
4862 operand: Air.Inst.Ref,
4863 inferred_alloc: *InferredAlloc,
4864) CompileError!void {
4865 // Create a store instruction as a placeholder. This will be replaced by a
4866 // proper store sequence once we know the stored type.
4867 const dummy_store = try block.addBinOp(.store, ptr, operand);
4868 try sema.checkComptimeKnownStore(block, dummy_store, src);
4869 // Add the stored instruction to the set we will use to resolve peer types
4870 // for the inferred allocation.
4871 try inferred_alloc.prongs.append(sema.arena, dummy_store.toIndex().?);
4872}
4873
4874fn storeToInferredAllocComptime(
4875 sema: *Sema,
4876 block: *Block,
4877 src: LazySrcLoc,
4878 operand: Air.Inst.Ref,
4879 iac: *Air.Inst.Data.InferredAllocComptime,
4880) CompileError!void {
4881 const pt = sema.pt;
4882 const zcu = pt.zcu;
4883 const operand_ty = sema.typeOf(operand);
4884 // There will be only one store_to_inferred_ptr because we are running at comptime.
4885 // The alloc will turn into a Decl or a ComptimeAlloc.
4886 const operand_val = sema.resolveValue(operand) orelse {
4887 return sema.failWithNeededComptime(block, src, .{ .simple = .stored_to_comptime_var });
4888 };
4889 const alloc_ty = try pt.ptrType(.{
4890 .child = operand_ty.toIntern(),
4891 .flags = .{
4892 .alignment = iac.alignment,
4893 .is_const = iac.is_const,
4894 },
4895 });
4896 if (operand_ty.classify(zcu) == .one_possible_value or
4897 (iac.is_const and !operand_val.canMutateComptimeVarState(zcu)))
4898 {
4899 iac.ptr = try pt.intern(.{ .ptr = .{
4900 .ty = alloc_ty.toIntern(),
4901 .base_addr = .{ .uav = .{
4902 .val = operand_val.toIntern(),
4903 .orig_ty = alloc_ty.toIntern(),
4904 } },
4905 .byte_offset = 0,
4906 } });
4907 } else {
4908 const alloc_index = try sema.newComptimeAlloc(block, src, operand_ty, iac.alignment);
4909 sema.getComptimeAlloc(alloc_index).val = .{ .interned = operand_val.toIntern() };
4910 iac.ptr = try pt.intern(.{ .ptr = .{
4911 .ty = alloc_ty.toIntern(),
4912 .base_addr = .{ .comptime_alloc = alloc_index },
4913 .byte_offset = 0,
4914 } });
4915 }
4916}
4917
4918fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
4919 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
4920 const src = block.nodeOffset(inst_data.src_node);
4921 const quota: u32 = @intCast(try sema.resolveInt(block, src, inst_data.operand, .u32, .{ .simple = .operand_setEvalBranchQuota }));
4922 sema.branch_quota = @max(sema.branch_quota, quota);
4923 sema.quota_request = @max(sema.quota_request, quota);
4924}
4925
4926fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
4927 const zir_tags = sema.code.instructions.items(.tag);
4928 const zir_datas = sema.code.instructions.items(.data);
4929 const inst_data = zir_datas[@backingInt(inst)].pl_node;
4930 const src = block.nodeOffset(inst_data.src_node);
4931 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
4932 const ptr = sema.resolveInst(extra.lhs);
4933 const operand = sema.resolveInst(extra.rhs);
4934
4935 const is_ret = if (extra.lhs.toIndex()) |ptr_index|
4936 zir_tags[@backingInt(ptr_index)] == .ret_ptr
4937 else
4938 false;
4939
4940 const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node });
4941 const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node });
4942 const air_tag: Air.Inst.Tag = if (is_ret)
4943 .ret_ptr
4944 else if (block.wantSafety())
4945 .store_safe
4946 else
4947 .store;
4948 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
4949}
4950
4951fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4952 const pt = sema.pt;
4953 const zcu = pt.zcu;
4954 const comp = zcu.comp;
4955 const gpa = comp.gpa;
4956 const io = comp.io;
4957 const ip = &zcu.intern_pool;
4958 const bytes = sema.code.instructions.items(.data)[@backingInt(inst)].str.get(sema.code);
4959 return sema.addStrLit(
4960 try ip.getOrPutString(gpa, io, pt.tid, bytes, .maybe_embedded_nulls),
4961 bytes.len,
4962 );
4963}
4964
4965fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) CompileError!Air.Inst.Ref {
4966 return sema.addStrLit(string.toString(), string.length(&sema.pt.zcu.intern_pool));
4967}
4968
4969pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref {
4970 const pt = sema.pt;
4971 const array_ty = try pt.arrayType(.{
4972 .len = len,
4973 .sentinel = .zero_u8,
4974 .child = .u8_type,
4975 });
4976 const val = try pt.intern(.{ .aggregate = .{
4977 .ty = array_ty.toIntern(),
4978 .storage = .{ .bytes = string },
4979 } });
4980 return sema.uavRef(.fromInterned(val));
4981}
4982
4983fn uavRef(sema: *Sema, val: Value) CompileError!Air.Inst.Ref {
4984 return .fromValue(try sema.pt.uavValue(val));
4985}
4986
4987fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4988 _ = block;
4989
4990 const int = sema.code.instructions.items(.data)[@backingInt(inst)].int;
4991 return sema.pt.intRef(.comptime_int, int);
4992}
4993
4994fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
4995 _ = block;
4996
4997 const int = sema.code.instructions.items(.data)[@backingInt(inst)].str;
4998 const byte_count = int.len * @sizeOf(std.math.big.Limb);
4999 const limb_bytes = sema.code.string_bytes[@backingInt(int.start)..][0..byte_count];
5000
5001 // TODO: this allocation and copy is only needed because the limbs may be unaligned.
5002 // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these
5003 // two lines can be removed.
5004 const limbs = try sema.arena.alloc(std.math.big.Limb, int.len);
5005 @memcpy(mem.sliceAsBytes(limbs), limb_bytes);
5006
5007 return Air.internedToRef((try sema.pt.intValue_big(.comptime_int, .{
5008 .limbs = limbs,
5009 .positive = true,
5010 })).toIntern());
5011}
5012
5013fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
5014 _ = block;
5015 const number = sema.code.instructions.items(.data)[@backingInt(inst)].float;
5016 return Air.internedToRef((try sema.pt.floatValue(
5017 .comptime_float,
5018 number,
5019 )).toIntern());
5020}
5021
5022fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
5023 _ = block;
5024 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
5025 const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
5026 const number = extra.get();
5027 return Air.internedToRef((try sema.pt.floatValue(.comptime_float, number)).toIntern());
5028}
5029
5030fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
5031 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
5032 const src = block.nodeOffset(inst_data.src_node);
5033 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
5034 const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .compile_error_string });
5035 return sema.fail(block, src, "{s}", .{msg});
5036}
5037
5038fn zirCompileLog(
5039 sema: *Sema,
5040 block: *Block,
5041 extended: Zir.Inst.Extended.InstData,
5042) CompileError!Air.Inst.Ref {
5043 const pt = sema.pt;
5044 const zcu = pt.zcu;
5045 const comp = zcu.comp;
5046 const gpa = comp.gpa;
5047 const io = comp.io;
5048
5049 var aw: std.Io.Writer.Allocating = .init(gpa);
5050 defer aw.deinit();
5051 const writer = &aw.writer;
5052
5053 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
5054 const src_node = extra.data.src_node;
5055 const args = sema.code.refSlice(extra.end, extended.small);
5056
5057 for (args, 0..) |arg_ref, i| {
5058 if (i != 0) writer.writeAll(", ") catch return error.OutOfMemory;
5059
5060 const arg = sema.resolveInst(arg_ref);
5061 const arg_ty = sema.typeOf(arg);
5062 if (sema.resolveValue(arg)) |val| {
5063 writer.print("@as({f}, {f})", .{
5064 arg_ty.fmt(pt), val.fmtValueSema(pt, sema),
5065 }) catch return error.OutOfMemory;
5066 } else {
5067 writer.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory;
5068 }
5069 }
5070
5071 const line_data = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, aw.written(), .no_embedded_nulls);
5072
5073 const line_idx: Zcu.CompileLogLine.Index = if (zcu.free_compile_log_lines.pop()) |idx| idx: {
5074 zcu.compile_log_lines.items[@backingInt(idx)] = .{
5075 .next = .none,
5076 .data = line_data,
5077 };
5078 break :idx idx;
5079 } else idx: {
5080 try zcu.compile_log_lines.append(gpa, .{
5081 .next = .none,
5082 .data = line_data,
5083 });
5084 break :idx @fromBackingInt(@intCast(zcu.compile_log_lines.items.len - 1));
5085 };
5086
5087 const gop = try zcu.compile_logs.getOrPut(gpa, sema.owner);
5088 if (gop.found_existing) {
5089 const prev_line = gop.value_ptr.last_line.get(zcu);
5090 assert(prev_line.next == .none);
5091 prev_line.next = line_idx.toOptional();
5092 gop.value_ptr.last_line = line_idx;
5093 } else {
5094 gop.value_ptr.* = .{
5095 .base_node_inst = block.src_base_inst,
5096 .node_offset = src_node,
5097 .first_line = line_idx,
5098 .last_line = line_idx,
5099 };
5100 }
5101 return .void_value;
5102}
5103
5104fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
5105 const pt = sema.pt;
5106 const zcu = pt.zcu;
5107
5108 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
5109 const src = block.nodeOffset(inst_data.src_node);
5110 const msg_inst = sema.resolveInst(inst_data.operand);
5111
5112 const arg_src = block.builtinCallArgSrc(inst_data.src_node, 0);
5113 const coerced_msg = try sema.coerce(block, .slice_const_u8, msg_inst, arg_src);
5114
5115 if (block.isComptime()) {
5116 const string = try sema.resolveConstString(block, arg_src, inst_data.operand, null);
5117 return sema.fail(block, src, "encountered @panic at comptime: {s}", .{string});
5118 }
5119
5120 // We only apply the first hint in a branch.
5121 // This allows user-provided hints to override implicit cold hints.
5122 if (sema.branch_hint == null) {
5123 sema.branch_hint = .cold;
5124 }
5125
5126 if (!zcu.backendSupportsFeature(.panic_fn)) {
5127 _ = try block.addNoOp(.trap);
5128 return;
5129 }
5130
5131 try sema.ensureMemoizedStateResolved(src, .panic);
5132 const panic_fn_index = zcu.std_lang_decl_values.get(.@"panic.call");
5133 const opt_usize_ty = try pt.optionalType(.usize_type);
5134 const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{
5135 .ty = opt_usize_ty.toIntern(),
5136 .val = .none,
5137 } })));
5138 // `callBuiltin` also calls `addReferenceEntry` to the function body for us.
5139 try sema.callBuiltin(
5140 block,
5141 src,
5142 .fromIntern(panic_fn_index),
5143 .auto,
5144 &.{ coerced_msg, null_ret_addr },
5145 .@"@panic",
5146 );
5147}
5148
5149fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
5150 const src_node = sema.code.instructions.items(.data)[@backingInt(inst)].node;
5151 const src = block.nodeOffset(src_node);
5152 if (block.isComptime())
5153 return sema.fail(block, src, "encountered @trap at comptime", .{});
5154 _ = try block.addNoOp(.trap);
5155}
5156
5157fn zirBreakpoint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
5158 const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand))));
5159 const src = block.nodeOffset(src_node);
5160 if (block.isComptime())
5161 return sema.fail(block, src, "encountered @breakpoint at comptime", .{});
5162 _ = try block.addNoOp(.breakpoint);
5163}
5164
5165fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
5166 const pt = sema.pt;
5167 const zcu = pt.zcu;
5168 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
5169 const src = parent_block.nodeOffset(inst_data.src_node);
5170 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
5171 const body = sema.code.bodySlice(extra.end, extra.data.body_len);
5172 const gpa = sema.gpa;
5173
5174 // AIR expects a block outside the loop block too.
5175 // Reserve space for a Loop instruction so that generated Break instructions can
5176 // point to it, even if it doesn't end up getting used because the code ends up being
5177 // comptime evaluated.
5178 const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
5179 const loop_inst: Air.Inst.Index = @fromBackingInt(@intCast(@backingInt(block_inst) + 1));
5180 try sema.air_instructions.ensureUnusedCapacity(gpa, 2);
5181 sema.air_instructions.appendAssumeCapacity(.{
5182 .tag = .block,
5183 .data = undefined,
5184 });
5185 sema.air_instructions.appendAssumeCapacity(.{
5186 .tag = .loop,
5187 .data = .{ .ty_pl = .{
5188 .ty = .noreturn,
5189 .payload = undefined,
5190 } },
5191 });
5192 var label: Block.Label = .{
5193 .zir_block = inst,
5194 .merges = .{
5195 .src_locs = .empty,
5196 .results = .empty,
5197 .br_list = .empty,
5198 .block_inst = block_inst,
5199 },
5200 };
5201 var child_block = parent_block.makeSubBlock();
5202 child_block.label = &label;
5203 child_block.runtime_cond = null;
5204 child_block.runtime_loop = src;
5205 child_block.runtime_index.increment();
5206 const merges = &child_block.label.?.merges;
5207
5208 defer child_block.instructions.deinit(gpa);
5209 defer merges.deinit(gpa);
5210
5211 var loop_block = child_block.makeSubBlock();
5212 defer loop_block.instructions.deinit(gpa);
5213
5214 // Use `analyzeBodyInner` directly to push any comptime control flow up the stack.
5215 try sema.analyzeBodyInner(&loop_block, body);
5216
5217 // TODO: since AIR has `repeat` now, we could change ZIR to generate
5218 // more optimal code utilizing `repeat` instructions across blocks!
5219 // For now, if the generated loop body does not terminate `noreturn`,
5220 // then `analyzeBodyInner` is signalling that it ended with `repeat`.
5221
5222 const loop_block_len = loop_block.instructions.items.len;
5223 if (loop_block_len > 0 and sema.typeOf(loop_block.instructions.items[loop_block_len - 1].toRef()).isNoReturn(zcu)) {
5224 // If the loop ended with a noreturn terminator, then there is no way for it to loop,
5225 // so we can just use the block instead.
5226 try child_block.instructions.appendSlice(gpa, loop_block.instructions.items);
5227 } else {
5228 _ = try loop_block.addInst(.{
5229 .tag = .repeat,
5230 .data = .{ .repeat = .{
5231 .loop_inst = loop_inst,
5232 } },
5233 });
5234 // Note that `loop_block_len` is now off by one.
5235
5236 try child_block.instructions.append(gpa, loop_inst);
5237
5238 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len + loop_block_len + 1);
5239 sema.air_instructions.items(.data)[@backingInt(loop_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(
5240 Air.Block{ .body_len = @intCast(loop_block_len + 1) },
5241 );
5242 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items));
5243 }
5244 return sema.resolveAnalyzedBlock(parent_block, src, &child_block, merges, false);
5245}
5246
5247fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
5248 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
5249 const src = parent_block.nodeOffset(inst_data.src_node);
5250 return sema.failWithUseOfAsync(parent_block, src);
5251}
5252
5253fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
5254 const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
5255 const src = parent_block.nodeOffset(pl_node.src_node);
5256 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
5257 const body = sema.code.bodySlice(extra.end, extra.data.body_len);
5258 const gpa = sema.gpa;
5259
5260 // Reserve space for a Block instruction so that generated Break instructions can
5261 // point to it, even if it doesn't end up getting used because the code ends up being
5262 // comptime evaluated or is an unlabeled block.
5263 const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
5264 try sema.air_instructions.append(gpa, .{
5265 .tag = .block,
5266 .data = undefined,
5267 });
5268
5269 var label: Block.Label = .{
5270 .zir_block = inst,
5271 .merges = .{
5272 .src_locs = .empty,
5273 .results = .empty,
5274 .br_list = .empty,
5275 .block_inst = block_inst,
5276 },
5277 };
5278
5279 var child_block: Block = .{
5280 .parent = parent_block,
5281 .sema = sema,
5282 .namespace = parent_block.namespace,
5283 .instructions = .empty,
5284 .label = &label,
5285 .inlining = parent_block.inlining,
5286 .comptime_reason = parent_block.comptime_reason,
5287 .is_typeof = parent_block.is_typeof,
5288 .want_safety = parent_block.want_safety,
5289 .float_mode = parent_block.float_mode,
5290 .runtime_cond = parent_block.runtime_cond,
5291 .runtime_loop = parent_block.runtime_loop,
5292 .runtime_index = parent_block.runtime_index,
5293 .error_return_trace_index = parent_block.error_return_trace_index,
5294 .src_base_inst = parent_block.src_base_inst,
5295 .type_name_ctx = parent_block.type_name_ctx,
5296 .type_fqn_ctx = parent_block.type_fqn_ctx,
5297 };
5298
5299 defer child_block.instructions.deinit(gpa);
5300 defer label.merges.deinit(gpa);
5301
5302 return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges);
5303}
5304
5305/// Semantically analyze the given ZIR body, emitting any resulting runtime code into the AIR block
5306/// specified by `child_block` if necessary (and emitting this block into `parent_block`).
5307/// TODO: `merges` is known from `child_block`, remove this parameter.
5308fn resolveBlockBody(
5309 sema: *Sema,
5310 parent_block: *Block,
5311 src: LazySrcLoc,
5312 child_block: *Block,
5313 body: []const Zir.Inst.Index,
5314 /// This is the instruction that a break instruction within `body` can
5315 /// use to return from the body.
5316 body_inst: Zir.Inst.Index,
5317 merges: *Block.Merges,
5318) CompileError!Air.Inst.Ref {
5319 if (child_block.isComptime()) {
5320 return sema.resolveInlineBody(child_block, body, body_inst);
5321 } else {
5322 assert(sema.air_instructions.items(.tag)[@backingInt(merges.block_inst)] == .block);
5323 var need_debug_scope = false;
5324 child_block.need_debug_scope = &need_debug_scope;
5325 if (sema.analyzeBodyInner(child_block, body)) {
5326 return sema.resolveAnalyzedBlock(parent_block, src, child_block, merges, need_debug_scope);
5327 } else |err| switch (err) {
5328 error.ComptimeBreak => {
5329 const break_inst = sema.comptime_break_inst;
5330 const break_data = sema.code.instructions.items(.data)[@backingInt(break_inst)].@"break";
5331 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
5332 const breaks_to_body = extra.block_inst == body_inst;
5333
5334 // Comptime control flow is happening, however child_block may still contain
5335 // runtime instructions which need to be copied to the parent block.
5336 if (need_debug_scope and child_block.instructions.items.len > 0) {
5337 // We need a runtime block for scoping reasons. The break
5338 // operand may have been produced by a runtime instruction
5339 // inside `child_blocks`.
5340 const operand = sema.resolveInst(break_data.operand);
5341 const operand_ty = sema.typeOf(operand);
5342 _ = try child_block.addBr(merges.block_inst, operand);
5343 try parent_block.instructions.append(sema.gpa, merges.block_inst);
5344 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).@"struct".field_names.len +
5345 child_block.instructions.items.len);
5346 sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{
5347 .ty = operand_ty,
5348 .payload = sema.addExtraAssumeCapacity(Air.Block{
5349 .body_len = @intCast(child_block.instructions.items.len),
5350 }),
5351 } };
5352 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
5353
5354 // The block result now holds the operand value, so we remap
5355 // the operand such that an enclosing scope which resolves
5356 // it picks up the block result rather than the internal
5357 // block instruction.
5358 if (break_data.operand.toIndex()) |operand_zir| {
5359 sema.inst_map.putAssumeCapacity(operand_zir, merges.block_inst.toRef());
5360 }
5361 if (breaks_to_body) {
5362 return merges.block_inst.toRef();
5363 } else {
5364 return error.ComptimeBreak;
5365 }
5366 } else {
5367 // We can copy instructions directly to the parent block.
5368 try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items);
5369 if (breaks_to_body) {
5370 return sema.resolveInst(break_data.operand);
5371 } else {
5372 return error.ComptimeBreak;
5373 }
5374 }
5375 },
5376 else => |e| return e,
5377 }
5378 }
5379}
5380
5381/// After a body corresponding to an AIR `block` has been analyzed, this function places them into
5382/// the block pointed at by `merges.block_inst` if necessary, or the block may be elided in favor of
5383/// inlining the instructions directly into the parent block. Either way, it considers all merges of
5384/// this block, and combines them appropriately using peer type resolution, returning the final
5385/// value of the block.
5386fn resolveAnalyzedBlock(
5387 sema: *Sema,
5388 parent_block: *Block,
5389 src: LazySrcLoc,
5390 child_block: *Block,
5391 merges: *Block.Merges,
5392 need_debug_scope: bool,
5393) CompileError!Air.Inst.Ref {
5394 const gpa = sema.gpa;
5395 const pt = sema.pt;
5396 const zcu = pt.zcu;
5397
5398 // Blocks must terminate with noreturn instruction.
5399 assert(child_block.instructions.items.len != 0);
5400 assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(zcu));
5401
5402 const block_tag = sema.air_instructions.items(.tag)[@backingInt(merges.block_inst)];
5403 switch (block_tag) {
5404 .block => {},
5405 .dbg_inline_block => assert(need_debug_scope),
5406 else => unreachable,
5407 }
5408 if (merges.results.items.len == 0) {
5409 switch (block_tag) {
5410 .block => {
5411 // No need for a block instruction. We can put the new instructions
5412 // directly into the parent block.
5413 if (need_debug_scope) {
5414 // The code following this block is unreachable, as the block has no
5415 // merges, so we don't necessarily need to emit this as an AIR block.
5416 // However, we need a block *somewhere* to make the scoping correct,
5417 // so forward this request to the parent block.
5418 if (parent_block.need_debug_scope) |ptr| ptr.* = true;
5419 }
5420 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items);
5421 return child_block.instructions.items[child_block.instructions.items.len - 1].toRef();
5422 },
5423 .dbg_inline_block => {
5424 // Create a block containing all instruction from the body.
5425 try parent_block.instructions.append(gpa, merges.block_inst);
5426 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".field_names.len +
5427 child_block.instructions.items.len);
5428 sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{
5429 .ty = .noreturn,
5430 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
5431 .func = child_block.inlining.?.func,
5432 .body_len = @intCast(child_block.instructions.items.len),
5433 }),
5434 } };
5435 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
5436 return merges.block_inst.toRef();
5437 },
5438 else => unreachable,
5439 }
5440 }
5441 if (merges.results.items.len == 1) {
5442 // If the `break` is trailing, we may be able to elide the AIR block here
5443 // by appending the new instructions directly to the parent block.
5444 if (!need_debug_scope) {
5445 const last_inst_index = child_block.instructions.items.len - 1;
5446 const last_inst = child_block.instructions.items[last_inst_index];
5447 if (sema.getBreakBlock(last_inst)) |br_block| {
5448 if (br_block == merges.block_inst) {
5449 // Great, the last instruction is the break! Put the instructions
5450 // directly into the parent block.
5451 try parent_block.instructions.appendSlice(gpa, child_block.instructions.items[0..last_inst_index]);
5452 return merges.results.items[0];
5453 }
5454 }
5455 }
5456 // Okay, we need a runtime block. If the value is comptime-known, the
5457 // block should just return void, and we return the merge result
5458 // directly. Otherwise, we can defer to the logic below.
5459 if (sema.resolveValue(merges.results.items[0])) |result_val| {
5460 // Create a block containing all instruction from the body.
5461 try parent_block.instructions.append(gpa, merges.block_inst);
5462 switch (block_tag) {
5463 .block => {
5464 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len +
5465 child_block.instructions.items.len);
5466 sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{
5467 .ty = .void,
5468 .payload = sema.addExtraAssumeCapacity(Air.Block{
5469 .body_len = @intCast(child_block.instructions.items.len),
5470 }),
5471 } };
5472 },
5473 .dbg_inline_block => {
5474 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".field_names.len +
5475 child_block.instructions.items.len);
5476 sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{
5477 .ty = .void,
5478 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
5479 .func = child_block.inlining.?.func,
5480 .body_len = @intCast(child_block.instructions.items.len),
5481 }),
5482 } };
5483 },
5484 else => unreachable,
5485 }
5486 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
5487 // Rewrite the break to just give value {}; the value is
5488 // comptime-known and will be returned directly.
5489 sema.air_instructions.items(.data)[@backingInt(merges.br_list.items[0])].br.operand = .void_value;
5490 return Air.internedToRef(result_val.toIntern());
5491 }
5492 }
5493 // It is impossible to have the number of results be > 1 in a comptime scope.
5494 assert(!child_block.isComptime()); // Should already got a compile error in the condbr condition.
5495
5496 // Note that we'll always create an AIR block here, so `need_debug_scope` is irrelevant.
5497
5498 // Need to set the type and emit the Block instruction. This allows machine code generation
5499 // to emit a jump instruction to after the block when it encounters the break.
5500 try parent_block.instructions.append(gpa, merges.block_inst);
5501 const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items });
5502 resolved_ty.assertHasLayout(zcu);
5503 // TODO add note "missing else causes void value"
5504
5505 const type_src = src; // TODO: better source location
5506 if (resolved_ty.comptimeOnly(zcu)) {
5507 const msg = msg: {
5508 const msg = try sema.errMsg(type_src, "value with comptime-only type '{f}' depends on runtime control flow", .{resolved_ty.fmt(pt)});
5509 errdefer msg.destroy(sema.gpa);
5510
5511 const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?;
5512 try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
5513
5514 try sema.explainWhyTypeIsComptime(msg, type_src, resolved_ty);
5515
5516 break :msg msg;
5517 };
5518 return sema.failWithOwnedErrorMsg(child_block, msg);
5519 }
5520 for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| {
5521 try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst);
5522 }
5523
5524 try sema.checkMergeAllowed(child_block, type_src, resolved_ty);
5525
5526 switch (block_tag) {
5527 .block => {
5528 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len +
5529 child_block.instructions.items.len);
5530 sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{
5531 .ty = resolved_ty,
5532 .payload = sema.addExtraAssumeCapacity(Air.Block{
5533 .body_len = @intCast(child_block.instructions.items.len),
5534 }),
5535 } };
5536 },
5537 .dbg_inline_block => {
5538 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".field_names.len +
5539 child_block.instructions.items.len);
5540 sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{
5541 .ty = resolved_ty,
5542 .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{
5543 .func = child_block.inlining.?.func,
5544 .body_len = @intCast(child_block.instructions.items.len),
5545 }),
5546 } };
5547 },
5548 else => unreachable,
5549 }
5550 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
5551 // Now that the block has its type resolved, we need to go back into all the break
5552 // instructions, and insert type coercion on the operands.
5553 for (merges.br_list.items) |br| {
5554 const br_operand = sema.air_instructions.items(.data)[@backingInt(br)].br.operand;
5555 const br_operand_src = src;
5556 const br_operand_ty = sema.typeOf(br_operand);
5557 if (br_operand_ty.eql(resolved_ty)) {
5558 // No type coercion needed.
5559 continue;
5560 }
5561 var coerce_block = parent_block.makeSubBlock();
5562 defer coerce_block.instructions.deinit(gpa);
5563 const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src);
5564 // If no instructions were produced, such as in the case of a coercion of a
5565 // constant value to a new type, we can simply point the br operand to it.
5566 if (coerce_block.instructions.items.len == 0) {
5567 sema.air_instructions.items(.data)[@backingInt(br)].br.operand = coerced_operand;
5568 continue;
5569 }
5570 assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1].toRef() == coerced_operand);
5571
5572 // Convert the br instruction to a block instruction that has the coercion
5573 // and then a new br inside that returns the coerced instruction.
5574 const sub_block_len: u32 = @intCast(coerce_block.instructions.items.len + 1);
5575 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len +
5576 sub_block_len);
5577 try sema.air_instructions.ensureUnusedCapacity(gpa, 1);
5578 const sub_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
5579
5580 sema.air_instructions.items(.tag)[@backingInt(br)] = .block;
5581 sema.air_instructions.items(.data)[@backingInt(br)] = .{ .ty_pl = .{
5582 .ty = .noreturn,
5583 .payload = sema.addExtraAssumeCapacity(Air.Block{
5584 .body_len = sub_block_len,
5585 }),
5586 } };
5587 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items));
5588 sema.air_extra.appendAssumeCapacity(@backingInt(sub_br_inst));
5589
5590 sema.air_instructions.appendAssumeCapacity(.{
5591 .tag = .br,
5592 .data = .{ .br = .{
5593 .block_inst = merges.block_inst,
5594 .operand = coerced_operand,
5595 } },
5596 });
5597 }
5598
5599 if (try resolved_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
5600 return merges.block_inst.toRef();
5601}
5602
5603fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
5604 const pt = sema.pt;
5605 const zcu = pt.zcu;
5606 const ip = &zcu.intern_pool;
5607 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
5608 const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
5609
5610 const src = block.nodeOffset(inst_data.src_node);
5611 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0);
5612 const options_src = block.builtinCallArgSrc(inst_data.src_node, 1);
5613
5614 const ptr = sema.resolveInst(extra.exported);
5615 const ptr_val = try sema.resolveConstDefinedValue(block, ptr_src, ptr, .{ .simple = .export_target });
5616 const ptr_ty = ptr_val.typeOf(zcu);
5617
5618 const options = try sema.resolveExportOptions(block, options_src, extra.options);
5619
5620 {
5621 if (ptr_ty.zigTypeTag(zcu) != .pointer) {
5622 return sema.fail(block, ptr_src, "expected pointer type, found '{f}'", .{ptr_ty.fmt(pt)});
5623 }
5624 const ptr_ty_info = ptr_ty.ptrInfo(zcu);
5625 if (ptr_ty_info.flags.size == .slice) {
5626 return sema.fail(block, ptr_src, "export target cannot be slice", .{});
5627 }
5628 if (ptr_ty_info.packed_offset.host_size != 0) {
5629 return sema.fail(block, ptr_src, "export target cannot be bit-pointer", .{});
5630 }
5631 }
5632
5633 const export_ty = ptr_ty.childType(zcu);
5634 try sema.ensureLayoutResolved(export_ty, src, .@"export");
5635 if (!export_ty.validateExtern(.other, zcu)) {
5636 return sema.failWithOwnedErrorMsg(block, msg: {
5637 const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)});
5638 errdefer msg.destroy(sema.gpa);
5639 try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other);
5640 try sema.addDeclaredHereNote(msg, export_ty);
5641 break :msg msg;
5642 });
5643 }
5644
5645 const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr;
5646 const target: Zcu.Exported = switch (ptr_info.base_addr) {
5647 .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}),
5648 .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}),
5649 .uav => |uav| .{ .uav = uav.val },
5650 .nav => |orig_nav| target: {
5651 try sema.ensureNavResolved(block, src, orig_nav, .fully);
5652 const export_nav = switch (ip.indexToKey(ip.getNav(orig_nav).resolved.?.value)) {
5653 .@"extern" => |e| e.owner_nav,
5654 .func => |f| f.owner_nav,
5655 else => orig_nav,
5656 };
5657 if (ip.getNav(export_nav).getExtern(ip) != null) {
5658 return sema.fail(block, src, "export target cannot be extern", .{});
5659 }
5660 try sema.maybeQueueFuncBodyAnalysis(block, src, export_nav);
5661 break :target .{ .nav = export_nav };
5662 },
5663 };
5664 if (ptr_info.byte_offset != 0) {
5665 return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{});
5666 }
5667 try sema.exports.append(zcu.gpa, .{
5668 .opts = options,
5669 .src = src,
5670 .exported = target,
5671 });
5672}
5673
5674/// Asserts that `sema.owner` is a `.nav_val` whose value is resolved.
5675///
5676/// Exports that `Nav` by the given name with all other options set to default.
5677pub fn analyzeExportSelfNav(
5678 sema: *Sema,
5679 block: *Block,
5680 src: LazySrcLoc,
5681 name: InternPool.NullTerminatedString,
5682) !void {
5683 const gpa = sema.gpa;
5684 const pt = sema.pt;
5685 const zcu = pt.zcu;
5686 const ip = &zcu.intern_pool;
5687
5688 const orig_nav = sema.owner.unwrap().nav_val;
5689 const export_val: Value = .fromInterned(ip.getNav(orig_nav).resolved.?.value);
5690 const export_ty = export_val.typeOf(zcu);
5691
5692 if (!export_ty.validateExtern(.other, zcu)) {
5693 return sema.failWithOwnedErrorMsg(block, msg: {
5694 const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)});
5695 errdefer msg.destroy(gpa);
5696 try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other);
5697 try sema.addDeclaredHereNote(msg, export_ty);
5698 break :msg msg;
5699 });
5700 }
5701
5702 const export_nav = switch (ip.indexToKey(export_val.toIntern())) {
5703 .@"extern" => |e| e.owner_nav,
5704 .func => |f| export_nav: {
5705 assert(export_ty.fnHasRuntimeBits(zcu)); // otherwise `validateExtern` failed above
5706 const orig_fn_index = ip.unwrapCoercedFunc(export_val.toIntern());
5707 try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index }));
5708 try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
5709 break :export_nav f.owner_nav;
5710 },
5711 else => orig_nav,
5712 };
5713
5714 try sema.exports.append(gpa, .{
5715 .opts = .{ .name = name },
5716 .src = src,
5717 .exported = .{ .nav = export_nav },
5718 });
5719}
5720
5721fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
5722 const pt = sema.pt;
5723 const zcu = pt.zcu;
5724 const io = zcu.comp.io;
5725 const ip = &zcu.intern_pool;
5726 const func = switch (sema.owner.unwrap()) {
5727 .func => |func| func,
5728 .@"comptime",
5729 .nav_val,
5730 .nav_ty,
5731 .type_layout,
5732 .struct_defaults,
5733 .memoized_state,
5734 => return, // does nothing outside a function
5735 };
5736 ip.funcSetDisableInstrumentation(io, func);
5737 sema.allow_memoize = false;
5738}
5739
5740fn zirDisableIntrinsics(sema: *Sema) CompileError!void {
5741 const pt = sema.pt;
5742 const zcu = pt.zcu;
5743 const io = zcu.comp.io;
5744 const ip = &zcu.intern_pool;
5745 const func = switch (sema.owner.unwrap()) {
5746 .func => |func| func,
5747 .@"comptime",
5748 .nav_val,
5749 .nav_ty,
5750 .type_layout,
5751 .struct_defaults,
5752 .memoized_state,
5753 => return, // does nothing outside a function
5754 };
5755 ip.funcSetDisableIntrinsics(io, func);
5756 sema.allow_memoize = false;
5757}
5758
5759fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
5760 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
5761 const src = block.builtinCallArgSrc(extra.node, 0);
5762 block.float_mode = try sema.resolveStdLangEnum(block, src, extra.operand, .FloatMode, .{ .simple = .operand_setFloatMode });
5763}
5764
5765fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
5766 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
5767 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
5768 block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, .{ .simple = .operand_setRuntimeSafety });
5769}
5770
5771fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
5772 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"break";
5773 const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
5774 const operand = sema.resolveInst(inst_data.operand);
5775 const zir_block = extra.block_inst;
5776
5777 var block = start_block;
5778 while (true) {
5779 if (block.label) |label| {
5780 if (label.zir_block == zir_block) {
5781 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
5782 const src_loc = if (extra.operand_src_node.unwrap()) |operand_src_node|
5783 start_block.nodeOffset(operand_src_node)
5784 else
5785 null;
5786 try label.merges.src_locs.append(sema.gpa, src_loc);
5787 try label.merges.results.append(sema.gpa, operand);
5788 try label.merges.br_list.append(sema.gpa, br_ref.toIndex().?);
5789 block.runtime_index.increment();
5790 if (block.runtime_cond == null and block.runtime_loop == null) {
5791 block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
5792 block.runtime_loop = start_block.runtime_loop;
5793 }
5794 return;
5795 }
5796 }
5797 block = block.parent.?;
5798 }
5799}
5800
5801fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
5802 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"break";
5803 const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
5804 const operand_src = start_block.nodeOffset(extra.operand_src_node.unwrap().?);
5805 const uncoerced_operand = sema.resolveInst(inst_data.operand);
5806 const switch_inst = extra.block_inst;
5807
5808 switch (sema.code.instructions.items(.tag)[@backingInt(switch_inst)]) {
5809 .switch_block, .switch_block_ref => {},
5810 .switch_block_err_union => unreachable, // wrong code path!
5811 else => unreachable, // assertion failure
5812 }
5813
5814 const operand_ty = (sema.resolveInst(switch_inst.toRef())).toType();
5815 const operand = try sema.coerce(start_block, operand_ty, uncoerced_operand, operand_src);
5816 try sema.validateRuntimeValue(start_block, operand_src, operand);
5817
5818 // We want to generate a `switch_dispatch` instruction with the switch condition,
5819 // possibly preceded by a store to the stack alloc containing the raw operand.
5820 // However, to avoid too much special-case state in Sema, this is handled by the
5821 // `switch` lowering logic. As such, we will find the `Block` corresponding to
5822 // the parent `switch_block[_ref]` instruction, create a dummy `br`, and add a
5823 // merge to signal to the switch logic to rewrite this into an appropriate dispatch.
5824
5825 var block = start_block;
5826 while (true) : (block = block.parent.?) {
5827 if (block.label) |label| {
5828 if (label.zir_block == switch_inst) {
5829 const br_ref = try start_block.addBr(label.merges.block_inst, operand);
5830 try label.merges.extra_insts.append(sema.gpa, br_ref.toIndex().?);
5831 try label.merges.extra_src_locs.append(sema.gpa, operand_src);
5832 block.runtime_index.increment();
5833 if (block.runtime_cond == null and block.runtime_loop == null) {
5834 block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop;
5835 block.runtime_loop = start_block.runtime_loop;
5836 }
5837 return;
5838 }
5839 }
5840 }
5841}
5842
5843fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
5844 if (block.isComptime() or block.ownerModule().strip) return;
5845
5846 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].dbg_stmt;
5847
5848 if (block.instructions.items.len != 0) {
5849 const idx = block.instructions.items[block.instructions.items.len - 1];
5850 if (sema.air_instructions.items(.tag)[@backingInt(idx)] == .dbg_stmt) {
5851 // The previous dbg_stmt didn't correspond to any actual code, so replace it.
5852 sema.air_instructions.items(.data)[@backingInt(idx)].dbg_stmt = .{
5853 .line = inst_data.line,
5854 .column = inst_data.column,
5855 };
5856 return;
5857 }
5858 }
5859
5860 _ = try block.addInst(.{
5861 .tag = .dbg_stmt,
5862 .data = .{ .dbg_stmt = .{
5863 .line = inst_data.line,
5864 .column = inst_data.column,
5865 } },
5866 });
5867}
5868
5869fn zirDbgEmptyStmt(_: *Sema, block: *Block, _: Zir.Inst.Index) CompileError!void {
5870 if (block.isComptime() or block.ownerModule().strip) return;
5871 _ = try block.addNoOp(.dbg_empty_stmt);
5872}
5873
5874fn zirDbgVar(
5875 sema: *Sema,
5876 block: *Block,
5877 inst: Zir.Inst.Index,
5878 air_tag: Air.Inst.Tag,
5879) CompileError!void {
5880 const str_op = sema.code.instructions.items(.data)[@backingInt(inst)].str_op;
5881 const operand = sema.resolveInst(str_op.operand);
5882 const name = str_op.getStr(sema.code);
5883 try sema.addDbgVar(block, operand, air_tag, name);
5884}
5885
5886fn addDbgVar(
5887 sema: *Sema,
5888 block: *Block,
5889 operand: Air.Inst.Ref,
5890 air_tag: Air.Inst.Tag,
5891 name: []const u8,
5892) CompileError!void {
5893 if (block.isComptime() or block.ownerModule().strip) return;
5894
5895 const pt = sema.pt;
5896 const zcu = pt.zcu;
5897 const operand_ty = sema.typeOf(operand);
5898 const val_ty = switch (air_tag) {
5899 .dbg_var_ptr => operand_ty.childType(zcu),
5900 .dbg_var_val, .dbg_arg_inline => operand_ty,
5901 else => unreachable,
5902 };
5903 if (val_ty.comptimeOnly(zcu)) return;
5904 if (!val_ty.hasRuntimeBits(zcu)) return;
5905 if (sema.resolveValue(operand)) |operand_val| {
5906 if (operand_val.canMutateComptimeVarState(zcu)) return;
5907 }
5908
5909 // To ensure the lexical scoping is known to backends, this alloc must be
5910 // within a real runtime block. We set a flag which communicates information
5911 // to the closest lexically enclosing block:
5912 // * If it is a `block_inline`, communicates to logic in `analyzeBodyInner`
5913 // to create a post-hoc block.
5914 // * Otherwise, communicates to logic in `resolveBlockBody` to create a
5915 // real `block` instruction.
5916 if (block.need_debug_scope) |ptr| ptr.* = true;
5917
5918 // Add the name to the AIR.
5919 const name_nts = try sema.appendAirString(name);
5920
5921 _ = try block.addInst(.{
5922 .tag = air_tag,
5923 .data = .{ .pl_op = .{
5924 .payload = @backingInt(name_nts),
5925 .operand = operand,
5926 } },
5927 });
5928}
5929
5930pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTerminatedString {
5931 if (str.len == 0) return .none;
5932 const nts: Air.NullTerminatedString = @fromBackingInt(@intCast(sema.air_extra.items.len));
5933 const elements_used = str.len / 4 + 1;
5934 const elements = try sema.air_extra.addManyAsSlice(sema.gpa, elements_used);
5935 const buffer = mem.sliceAsBytes(elements);
5936 @memcpy(buffer[0..str.len], str);
5937 buffer[str.len] = 0;
5938 return nts;
5939}
5940
5941fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
5942 const pt = sema.pt;
5943 const zcu = pt.zcu;
5944 const comp = zcu.comp;
5945 const gpa = comp.gpa;
5946 const io = comp.io;
5947
5948 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok;
5949 const src = block.tokenOffset(inst_data.src_tok);
5950 const decl_name = try zcu.intern_pool.getOrPutString(
5951 gpa,
5952 io,
5953 pt.tid,
5954 inst_data.get(sema.code),
5955 .no_embedded_nulls,
5956 );
5957 const nav_index = try sema.lookupIdentifier(block, decl_name);
5958 return sema.analyzeNavRef(block, src, nav_index);
5959}
5960
5961fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
5962 const pt = sema.pt;
5963 const zcu = pt.zcu;
5964 const comp = zcu.comp;
5965 const gpa = comp.gpa;
5966 const io = comp.io;
5967
5968 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok;
5969 const src = block.tokenOffset(inst_data.src_tok);
5970 const decl_name = try zcu.intern_pool.getOrPutString(
5971 gpa,
5972 io,
5973 pt.tid,
5974 inst_data.get(sema.code),
5975 .no_embedded_nulls,
5976 );
5977 const nav = try sema.lookupIdentifier(block, decl_name);
5978 return sema.analyzeNavVal(block, src, nav);
5979}
5980
5981fn lookupIdentifier(sema: *Sema, block: *Block, name: InternPool.NullTerminatedString) !InternPool.Nav.Index {
5982 const pt = sema.pt;
5983 const zcu = pt.zcu;
5984 var namespace = block.namespace;
5985 while (true) {
5986 if (try sema.lookupInNamespace(block, namespace, name)) |lookup| {
5987 assert(lookup.accessible == .public or lookup.accessible == .private_same_file);
5988 return lookup.nav;
5989 }
5990 namespace = zcu.namespacePtr(namespace).parent.unwrap() orelse break;
5991 }
5992 unreachable; // AstGen detects use of undeclared identifiers.
5993}
5994
5995/// This looks up a member of a specific namespace.
5996fn lookupInNamespace(
5997 sema: *Sema,
5998 block: *Block,
5999 namespace_index: InternPool.NamespaceIndex,
6000 ident_name: InternPool.NullTerminatedString,
6001) CompileError!?struct {
6002 nav: InternPool.Nav.Index,
6003 accessible: enum { public, private_same_file, private },
6004} {
6005 const pt = sema.pt;
6006 const zcu = pt.zcu;
6007
6008 pt.ensureNamespaceUpToDate(namespace_index) catch |err| switch (err) {
6009 error.LostZirContainerDecl => {
6010 const namespace = zcu.namespacePtr(namespace_index);
6011 const ns_ty: Type = .fromInterned(namespace.owner_type);
6012 return sema.failTransitive(.{ .lost_tracking = ns_ty.typeDeclInstAllowGeneratedTag(zcu).? });
6013 },
6014 else => |e| return e,
6015 };
6016
6017 const namespace = zcu.namespacePtr(namespace_index);
6018
6019 const adapter: Zcu.Namespace.NameAdapter = .{ .zcu = zcu };
6020
6021 const src_file = zcu.namespacePtr(block.namespace).file_scope;
6022
6023 if (Type.fromInterned(namespace.owner_type).typeDeclInst(zcu)) |type_decl_inst| {
6024 try sema.declareDependency(.{ .namespace_name = .{
6025 .namespace = type_decl_inst,
6026 .name = ident_name,
6027 } });
6028 }
6029
6030 if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
6031 return .{
6032 .nav = nav_index,
6033 .accessible = .public,
6034 };
6035 } else if (namespace.priv_decls.getKeyAdapted(ident_name, adapter)) |nav_index| {
6036 return .{
6037 .nav = nav_index,
6038 .accessible = if (src_file == namespace.file_scope) .private_same_file else .private,
6039 };
6040 }
6041
6042 return null;
6043}
6044
6045fn funcDeclSrcInst(sema: *Sema, func_inst: Air.Inst.Ref) !?InternPool.TrackedInst.Index {
6046 const pt = sema.pt;
6047 const zcu = pt.zcu;
6048 const ip = &zcu.intern_pool;
6049 const func_val = sema.resolveValue(func_inst) orelse return null;
6050 if (func_val.isUndef(zcu)) return null;
6051 const nav = switch (ip.indexToKey(func_val.toIntern())) {
6052 .@"extern" => |e| e.owner_nav,
6053 .func => |f| f.owner_nav,
6054 .ptr => |ptr| switch (ptr.base_addr) {
6055 .nav => |nav| if (ptr.byte_offset == 0) nav else return null,
6056 else => return null,
6057 },
6058 else => return null,
6059 };
6060 return ip.getNav(nav).srcInst(ip);
6061}
6062
6063pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref {
6064 const pt = sema.pt;
6065 const zcu = pt.zcu;
6066 const comp = zcu.comp;
6067 const gpa = comp.gpa;
6068 const io = comp.io;
6069
6070 if (block.isComptime() or block.is_typeof) {
6071 const index_val = try pt.intValue_u64(.usize, sema.comptime_err_ret_trace.items.len);
6072 return Air.internedToRef(index_val.toIntern());
6073 }
6074
6075 if (!block.ownerModule().error_tracing) return .none;
6076
6077 const stack_trace_ty = try sema.getStdLangType(block.nodeOffset(.zero), .StackTrace);
6078 const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
6079 const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) {
6080 error.AlreadyReported => @panic("std.lang.StackTrace is corrupt"),
6081 error.ComptimeReturn, error.ComptimeBreak => unreachable,
6082 error.OutOfMemory, error.Canceled => |e| return e,
6083 };
6084
6085 return try block.addInst(.{
6086 .tag = .save_err_return_trace_index,
6087 .data = .{ .ty_pl = .{
6088 .ty = stack_trace_ty,
6089 .payload = @intCast(field_index),
6090 } },
6091 });
6092}
6093
6094/// Add instructions to block to "pop" the error return trace.
6095/// If `operand` is provided, only pops if operand is non-error.
6096fn popErrorReturnTrace(
6097 sema: *Sema,
6098 block: *Block,
6099 src: LazySrcLoc,
6100 operand: Air.Inst.Ref,
6101 saved_error_trace_index: Air.Inst.Ref,
6102) CompileError!void {
6103 const pt = sema.pt;
6104 const zcu = pt.zcu;
6105 const comp = zcu.comp;
6106 const gpa = comp.gpa;
6107 const io = comp.io;
6108 var is_non_error: ?bool = null;
6109 var is_non_error_inst: Air.Inst.Ref = undefined;
6110 if (operand != .none) {
6111 is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand);
6112 if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val|
6113 is_non_error = cond_val.toBool();
6114 } else is_non_error = true; // no operand means pop unconditionally
6115
6116 if (is_non_error == true) {
6117 // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or
6118 // the result is comptime-known to be a non-error. Either way, pop unconditionally.
6119
6120 const stack_trace_ty = try sema.getStdLangType(src, .StackTrace);
6121 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
6122 const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty);
6123 const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
6124 const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty);
6125 try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store);
6126 } else if (is_non_error == null) {
6127 // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need
6128 // to pop any error trace that may have been propagated from our arguments.
6129
6130 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len);
6131 const cond_block_inst = try block.addInstAsIndex(.{
6132 .tag = .block,
6133 .data = .{
6134 .ty_pl = .{
6135 .ty = .void,
6136 .payload = undefined, // updated below
6137 },
6138 },
6139 });
6140
6141 var then_block = block.makeSubBlock();
6142 defer then_block.instructions.deinit(gpa);
6143
6144 // If non-error, then pop the error return trace by restoring the index.
6145 const stack_trace_ty = try sema.getStdLangType(src, .StackTrace);
6146 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
6147 const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty);
6148 const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
6149 const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty);
6150 try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store);
6151 _ = try then_block.addBr(cond_block_inst, .void_value);
6152
6153 // Otherwise, do nothing
6154 var else_block = block.makeSubBlock();
6155 defer else_block.instructions.deinit(gpa);
6156 _ = try else_block.addBr(cond_block_inst, .void_value);
6157
6158 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".field_names.len +
6159 then_block.instructions.items.len + else_block.instructions.items.len +
6160 @typeInfo(Air.Block).@"struct".field_names.len + 1); // +1 for the sole .cond_br instruction in the .block
6161
6162 const cond_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
6163 try sema.air_instructions.append(gpa, .{
6164 .tag = .cond_br,
6165 .data = .{
6166 .pl_op = .{
6167 .operand = is_non_error_inst,
6168 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
6169 .then_body_len = @intCast(then_block.instructions.items.len),
6170 .else_body_len = @intCast(else_block.instructions.items.len),
6171 .branch_hints = .{
6172 // Weight against error branch.
6173 .true = .likely,
6174 .false = .unlikely,
6175 // Code coverage is not valuable on either branch.
6176 .then_cov = .none,
6177 .else_cov = .none,
6178 },
6179 }),
6180 },
6181 },
6182 });
6183 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
6184 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
6185
6186 sema.air_instructions.items(.data)[@backingInt(cond_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 });
6187 sema.air_extra.appendAssumeCapacity(@backingInt(cond_br_inst));
6188 }
6189}
6190
6191fn zirCall(
6192 sema: *Sema,
6193 block: *Block,
6194 inst: Zir.Inst.Index,
6195 comptime kind: enum { direct, field },
6196) CompileError!Air.Inst.Ref {
6197 const pt = sema.pt;
6198 const zcu = pt.zcu;
6199 const comp = zcu.comp;
6200 const gpa = comp.gpa;
6201 const io = comp.io;
6202
6203 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
6204 const callee_src = block.src(.{ .node_offset_call_func = inst_data.src_node });
6205 const call_src = block.nodeOffset(inst_data.src_node);
6206 const ExtraType = switch (kind) {
6207 .direct => Zir.Inst.Call,
6208 .field => Zir.Inst.FieldCall,
6209 };
6210 const extra = sema.code.extraData(ExtraType, inst_data.payload_index);
6211 const args_len = extra.data.flags.args_len;
6212
6213 const modifier: std.lang.CallModifier = @fromBackingInt(@intCast(extra.data.flags.packed_modifier));
6214 const ensure_result_used = extra.data.flags.ensure_result_used;
6215 const pop_error_return_trace = extra.data.flags.pop_error_return_trace;
6216
6217 const callee: ResolvedFieldCallee = switch (kind) {
6218 .direct => .{ .direct = sema.resolveInst(extra.data.callee) },
6219 .field => blk: {
6220 const object_ptr = sema.resolveInst(extra.data.obj_ptr);
6221 const field_name = try zcu.intern_pool.getOrPutString(
6222 gpa,
6223 io,
6224 pt.tid,
6225 sema.code.nullTerminatedString(extra.data.field_name_start),
6226 .no_embedded_nulls,
6227 );
6228 const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
6229 break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src);
6230 },
6231 };
6232 const func: Air.Inst.Ref = switch (callee) {
6233 .direct => |func_inst| func_inst,
6234 .method => |method| method.func_inst,
6235 };
6236
6237 const callee_ty = sema.typeOf(func);
6238 const total_args = args_len + @intFromBool(callee == .method);
6239 const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, callee == .method);
6240
6241 // The block index before the call, so we can potentially insert an error trace save here later.
6242 const block_index: Air.Inst.Index = @fromBackingInt(@intCast(block.instructions.items.len));
6243
6244 // This will be set by `analyzeCall` to indicate whether any parameter was an error (making the
6245 // error trace potentially dirty).
6246 var input_is_error = false;
6247
6248 const args_info: CallArgsInfo = .{ .zir_call = .{
6249 .bound_arg = switch (callee) {
6250 .direct => .none,
6251 .method => |method| method.arg0_inst,
6252 },
6253 .bound_arg_src = callee_src,
6254 .call_inst = inst,
6255 .call_node_offset = inst_data.src_node,
6256 .num_args = args_len,
6257 .args_body = @ptrCast(sema.code.extra[extra.end..]),
6258 .any_arg_is_error = &input_is_error,
6259 } };
6260
6261 // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction.
6262 const call_dbg_node: Zir.Inst.Index = @fromBackingInt(@intCast(@backingInt(inst) - 1));
6263 const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
6264
6265 if (block.ownerModule().error_tracing and
6266 !block.isComptime() and !block.is_typeof and (input_is_error or pop_error_return_trace))
6267 {
6268 const return_ty = sema.typeOf(call_inst);
6269 if (modifier != .always_tail and return_ty.isNoReturn(zcu))
6270 return call_inst; // call to "fn (...) noreturn", don't pop
6271
6272 // TODO: we don't fix up the error trace for always_tail correctly, we should be doing it
6273 // *before* the recursive call. This will be a bit tricky to do and probably requires
6274 // moving this logic into analyzeCall. But that's probably a good idea anyway.
6275 if (modifier == .always_tail)
6276 return call_inst;
6277
6278 // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only
6279 // need to clean-up our own trace if we were passed to a non-error-handling expression.
6280 if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) {
6281 const stack_trace_ty = try sema.getStdLangType(call_src, .StackTrace);
6282 const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls);
6283 const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src);
6284
6285 // Insert a save instruction before the arg resolution + call instructions we just generated
6286 const save_inst = try block.insertInst(block_index, .{
6287 .tag = .save_err_return_trace_index,
6288 .data = .{ .ty_pl = .{
6289 .ty = stack_trace_ty,
6290 .payload = @intCast(field_index),
6291 } },
6292 });
6293
6294 // Pop the error return trace, testing the result for non-error if necessary
6295 const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst;
6296 try sema.popErrorReturnTrace(block, call_src, operand, save_inst);
6297 }
6298
6299 return call_inst;
6300 } else {
6301 return call_inst;
6302 }
6303}
6304
6305fn checkCallArgumentCount(
6306 sema: *Sema,
6307 block: *Block,
6308 func: Air.Inst.Ref,
6309 func_src: LazySrcLoc,
6310 callee_ty: Type,
6311 total_args: usize,
6312 member_fn: bool,
6313) !Type {
6314 const pt = sema.pt;
6315 const zcu = pt.zcu;
6316 const func_ty: Type = func_ty: {
6317 switch (callee_ty.zigTypeTag(zcu)) {
6318 .@"fn" => break :func_ty callee_ty,
6319 .pointer => {
6320 const ptr_info = callee_ty.ptrInfo(zcu);
6321 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") {
6322 break :func_ty .fromInterned(ptr_info.child);
6323 }
6324 },
6325 .optional => {
6326 const opt_child = callee_ty.optionalChild(zcu);
6327 if (opt_child.zigTypeTag(zcu) == .@"fn" or (opt_child.isSinglePointer(zcu) and
6328 opt_child.childType(zcu).zigTypeTag(zcu) == .@"fn"))
6329 {
6330 const msg = msg: {
6331 const msg = try sema.errMsg(func_src, "cannot call optional type '{f}'", .{
6332 callee_ty.fmt(pt),
6333 });
6334 errdefer msg.destroy(sema.gpa);
6335 try sema.errNote(func_src, msg, "consider using '.?', 'orelse' or 'if'", .{});
6336 break :msg msg;
6337 };
6338 return sema.failWithOwnedErrorMsg(block, msg);
6339 }
6340 },
6341 else => {},
6342 }
6343 return sema.fail(block, func_src, "type '{f}' not a function", .{callee_ty.fmt(pt)});
6344 };
6345
6346 const func_ty_info = zcu.typeToFunc(func_ty).?;
6347 const fn_params_len = func_ty_info.param_types.len;
6348 const args_len = total_args - @intFromBool(member_fn);
6349 if (func_ty_info.is_var_args) {
6350 assert(callConvSupportsVarArgs(func_ty_info.cc));
6351 if (total_args >= fn_params_len) return func_ty;
6352 } else if (fn_params_len == total_args) {
6353 return func_ty;
6354 }
6355
6356 const maybe_func_inst = try sema.funcDeclSrcInst(func);
6357 const member_str = if (member_fn) "member function " else "";
6358 const variadic_str = if (func_ty_info.is_var_args) "at least " else "";
6359 const msg = msg: {
6360 const msg = try sema.errMsg(
6361 func_src,
6362 "{s}expected {s}{d} argument(s), found {d}",
6363 .{
6364 member_str,
6365 variadic_str,
6366 fn_params_len - @intFromBool(member_fn),
6367 args_len,
6368 },
6369 );
6370 errdefer msg.destroy(sema.gpa);
6371
6372 if (maybe_func_inst) |func_inst| {
6373 try sema.errNote(.{
6374 .base_node_inst = func_inst,
6375 .offset = LazySrcLoc.Offset.nodeOffset(.zero),
6376 }, msg, "function declared here", .{});
6377 }
6378 break :msg msg;
6379 };
6380 return sema.failWithOwnedErrorMsg(block, msg);
6381}
6382
6383fn callBuiltin(
6384 sema: *Sema,
6385 block: *Block,
6386 call_src: LazySrcLoc,
6387 builtin_fn: Air.Inst.Ref,
6388 modifier: std.lang.CallModifier,
6389 args: []const Air.Inst.Ref,
6390 operation: CallOperation,
6391) !void {
6392 const pt = sema.pt;
6393 const zcu = pt.zcu;
6394 const callee_ty = sema.typeOf(builtin_fn);
6395 const func_ty: Type = func_ty: {
6396 switch (callee_ty.zigTypeTag(zcu)) {
6397 .@"fn" => break :func_ty callee_ty,
6398 .pointer => {
6399 const ptr_info = callee_ty.ptrInfo(zcu);
6400 if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") {
6401 break :func_ty .fromInterned(ptr_info.child);
6402 }
6403 },
6404 else => {},
6405 }
6406 std.debug.panic("type '{f}' is not a function calling builtin fn", .{callee_ty.fmt(pt)});
6407 };
6408
6409 const func_ty_info = zcu.typeToFunc(func_ty).?;
6410 const fn_params_len = func_ty_info.param_types.len;
6411 if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) {
6412 std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len });
6413 }
6414
6415 _ = try sema.analyzeCall(
6416 block,
6417 builtin_fn,
6418 func_ty,
6419 call_src,
6420 call_src,
6421 modifier,
6422 false,
6423 .{ .resolved = .{ .src = call_src, .args = args } },
6424 null,
6425 operation,
6426 );
6427}
6428
6429const CallOperation = enum {
6430 call,
6431 @"@call",
6432 @"@panic",
6433 @"safety check",
6434 @"error return",
6435};
6436
6437const CallArgsInfo = union(enum) {
6438 /// The full list of resolved (but uncoerced) arguments is known ahead of time.
6439 resolved: struct {
6440 src: LazySrcLoc,
6441 args: []const Air.Inst.Ref,
6442 },
6443
6444 /// The list of resolved (but uncoerced) arguments is known ahead of time, but
6445 /// originated from a usage of the @call builtin at the given node offset.
6446 call_builtin: struct {
6447 call_node_offset: std.zig.Ast.Node.Offset,
6448 args: []const Air.Inst.Ref,
6449 },
6450
6451 /// This call corresponds to a ZIR call instruction. The arguments have not yet been
6452 /// resolved. They must be resolved by `analyzeCall` so that argument resolution and
6453 /// generic instantiation may be interleaved. This is required for RLS to work on
6454 /// generic parameters.
6455 zir_call: struct {
6456 /// This may be `none`, in which case it is ignored. Otherwise, it is the
6457 /// already-resolved value of the first argument, from method call syntax.
6458 bound_arg: Air.Inst.Ref,
6459 /// The source location of `bound_arg` if it is not `null`. Otherwise `undefined`.
6460 bound_arg_src: LazySrcLoc,
6461 /// The ZIR call instruction. The parameter type is placed at this index while
6462 /// analyzing arguments.
6463 call_inst: Zir.Inst.Index,
6464 /// The node offset of `call_inst`.
6465 call_node_offset: std.zig.Ast.Node.Offset,
6466 /// The number of arguments to this call, not including `bound_arg`.
6467 num_args: u32,
6468 /// The ZIR corresponding to all function arguments (other than `bound_arg`, if it
6469 /// is not `none`). Format is precisely the same as trailing data of ZIR `call`.
6470 args_body: []const Zir.Inst.Index,
6471 /// This bool will be set to true if any argument evaluated turns out to have an error set or error union type.
6472 /// This is used by the caller to restore the error return trace when necessary.
6473 any_arg_is_error: *bool,
6474 },
6475
6476 fn count(cai: CallArgsInfo) usize {
6477 return switch (cai) {
6478 inline .resolved, .call_builtin => |resolved| resolved.args.len,
6479 .zir_call => |zir_call| zir_call.num_args + @intFromBool(zir_call.bound_arg != .none),
6480 };
6481 }
6482
6483 fn argSrc(cai: CallArgsInfo, block: *Block, arg_index: usize) LazySrcLoc {
6484 return switch (cai) {
6485 .resolved => |resolved| resolved.src,
6486 .call_builtin => |call_builtin| block.src(.{ .call_arg = .{
6487 .call_node_offset = call_builtin.call_node_offset,
6488 .arg_index = @intCast(arg_index),
6489 } }),
6490 .zir_call => |zir_call| if (arg_index == 0 and zir_call.bound_arg != .none) {
6491 return zir_call.bound_arg_src;
6492 } else block.src(.{ .call_arg = .{
6493 .call_node_offset = zir_call.call_node_offset,
6494 .arg_index = @intCast(arg_index - @intFromBool(zir_call.bound_arg != .none)),
6495 } }),
6496 };
6497 }
6498
6499 /// Analyzes the arg at `arg_index` and coerces it to `param_ty`.
6500 /// `param_ty` may be `generic_poison`. A value of `null` indicates a varargs parameter.
6501 /// `func_ty_info` may be the type before instantiation, even if a generic instantiation is in progress.
6502 /// Emits a compile error if the argument is not comptime-known despite either `block.isComptime()` or
6503 /// the parameter being marked `comptime`.
6504 fn analyzeArg(
6505 cai: CallArgsInfo,
6506 sema: *Sema,
6507 block: *Block,
6508 arg_index: usize,
6509 maybe_param_ty: ?Type,
6510 func_ty_info: InternPool.Key.FuncType,
6511 func_inst: Air.Inst.Ref,
6512 maybe_func_src_inst: ?InternPool.TrackedInst.Index,
6513 ) CompileError!Air.Inst.Ref {
6514 const pt = sema.pt;
6515 const zcu = pt.zcu;
6516 const param_count = func_ty_info.param_types.len;
6517 const uncoerced_arg: Air.Inst.Ref = switch (cai) {
6518 inline .resolved, .call_builtin => |resolved| resolved.args[arg_index],
6519 .zir_call => |zir_call| arg_val: {
6520 // Generate args to comptime params in comptime block
6521 const parent_comptime = block.comptime_reason;
6522 defer block.comptime_reason = parent_comptime;
6523 // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx`
6524 if (std.math.cast(u5, arg_index)) |i| {
6525 if (i < param_count and func_ty_info.paramIsComptime(i)) {
6526 block.comptime_reason = .{
6527 .reason = .{
6528 .src = cai.argSrc(block, arg_index),
6529 .r = .{
6530 .comptime_param = .{
6531 .comptime_src = if (maybe_func_src_inst) |src_inst| .{
6532 .base_node_inst = src_inst,
6533 .offset = .{ .func_decl_param_comptime = @intCast(arg_index) },
6534 } else unreachable, // should be non-null because the function is generic
6535 },
6536 },
6537 },
6538 };
6539 }
6540 }
6541
6542 const has_bound_arg = zir_call.bound_arg != .none;
6543 const uncoerced_arg = if (arg_index == 0 and has_bound_arg) zir_call.bound_arg else arg: {
6544 const real_arg_idx = arg_index - @intFromBool(has_bound_arg);
6545
6546 const arg_body = if (real_arg_idx == 0) blk: {
6547 const start = zir_call.num_args;
6548 const end = @backingInt(zir_call.args_body[0]);
6549 break :blk zir_call.args_body[start..end];
6550 } else blk: {
6551 const start = @backingInt(zir_call.args_body[real_arg_idx - 1]);
6552 const end = @backingInt(zir_call.args_body[real_arg_idx]);
6553 break :blk zir_call.args_body[start..end];
6554 };
6555
6556 // Give the arg its result type
6557 const provide_param_ty: Type = maybe_param_ty orelse .generic_poison;
6558 sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(provide_param_ty.toIntern()));
6559 // Resolve the arg!
6560 break :arg try sema.resolveInlineBody(block, arg_body, zir_call.call_inst);
6561 };
6562
6563 if (block.isComptime() and !try sema.isComptimeKnown(uncoerced_arg)) {
6564 return sema.failWithNeededComptime(block, cai.argSrc(block, arg_index), null);
6565 }
6566
6567 if (sema.typeOf(uncoerced_arg).classify(zcu) == .no_possible_value) {
6568 // This terminates resolution of arguments. The caller should
6569 // propagate this.
6570 return uncoerced_arg;
6571 }
6572
6573 if (sema.typeOf(uncoerced_arg).isError(zcu)) {
6574 zir_call.any_arg_is_error.* = true;
6575 }
6576
6577 break :arg_val uncoerced_arg;
6578 },
6579 };
6580 const param_ty = maybe_param_ty orelse {
6581 return sema.coerceVarArgParam(block, uncoerced_arg, cai.argSrc(block, arg_index));
6582 };
6583 switch (param_ty.toIntern()) {
6584 .generic_poison_type => return uncoerced_arg,
6585 else => return sema.coerceExtra(
6586 block,
6587 param_ty,
6588 uncoerced_arg,
6589 cai.argSrc(block, arg_index),
6590 .{ .param_src = .{
6591 .func_inst = func_inst,
6592 .param_i = @intCast(arg_index),
6593 } },
6594 ) catch |err| switch (err) {
6595 error.NotCoercible => unreachable,
6596 else => |e| return e,
6597 },
6598 }
6599 }
6600};
6601
6602fn analyzeCall(
6603 sema: *Sema,
6604 block: *Block,
6605 callee: Air.Inst.Ref,
6606 func_ty: Type,
6607 func_src: LazySrcLoc,
6608 call_src: LazySrcLoc,
6609 modifier: std.lang.CallModifier,
6610 ensure_result_used: bool,
6611 args_info: CallArgsInfo,
6612 call_dbg_node: ?Zir.Inst.Index,
6613 operation: CallOperation,
6614) CompileError!Air.Inst.Ref {
6615 const pt = sema.pt;
6616 const zcu = pt.zcu;
6617 const comp = zcu.comp;
6618 const gpa = comp.gpa;
6619 const io = comp.io;
6620 const ip = &zcu.intern_pool;
6621 const arena = sema.arena;
6622
6623 const maybe_func_inst = try sema.funcDeclSrcInst(callee);
6624 const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{
6625 .base_node_inst = fn_decl_inst,
6626 .offset = .{ .node_offset_fn_type_ret_ty = .zero },
6627 } else func_src;
6628
6629 const func_ty_info = zcu.typeToFunc(func_ty).?;
6630
6631 for (func_ty_info.param_types.get(ip), 0..) |param_ty_ip, param_index| {
6632 const arg_src = args_info.argSrc(block, param_index);
6633 try sema.ensureLayoutResolved(.fromInterned(param_ty_ip), arg_src, .init);
6634 }
6635 try sema.ensureLayoutResolved(.fromInterned(func_ty_info.return_type), func_ret_ty_src, .return_type);
6636 try sema.validateResolvedFuncType(
6637 block,
6638 func_ty_info.cc,
6639 func_ty_info.param_types.get(ip),
6640 .fromInterned(func_ty_info.return_type),
6641 func_src,
6642 maybe_func_inst,
6643 );
6644
6645 if (!callConvIsCallable(func_ty_info.cc)) {
6646 return sema.failWithOwnedErrorMsg(block, msg: {
6647 const msg = try sema.errMsg(
6648 func_src,
6649 "unable to call function with calling convention '{s}'",
6650 .{@tagName(func_ty_info.cc)},
6651 );
6652 errdefer msg.destroy(gpa);
6653 if (maybe_func_inst) |func_inst| try sema.errNote(.{
6654 .base_node_inst = func_inst,
6655 .offset = .nodeOffset(.zero),
6656 }, msg, "function declared here", .{});
6657 break :msg msg;
6658 });
6659 }
6660
6661 const any_comptime_params = func_ty_info.comptime_bits != 0 or ct: {
6662 for (func_ty_info.param_types.get(ip)) |param_ty| {
6663 if (Type.fromInterned(param_ty).comptimeOnly(zcu)) break :ct true;
6664 }
6665 break :ct Type.fromInterned(func_ty_info.return_type).comptimeOnly(zcu);
6666 };
6667 const any_generic_types = generic: {
6668 for (func_ty_info.param_types.get(ip)) |param_ty| {
6669 if (param_ty == .generic_poison_type) break :generic true;
6670 }
6671 const ret_ty: Type = .fromInterned(func_ty_info.return_type);
6672 if (ret_ty.toIntern() == .generic_poison_type) {
6673 break :generic true;
6674 }
6675 if (ret_ty.zigTypeTag(zcu) == .error_union and
6676 ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type)
6677 {
6678 break :generic true;
6679 }
6680 break :generic false;
6681 };
6682
6683 // We need this value in a few code paths.
6684 const callee_val = try sema.resolveDefinedValue(block, call_src, callee);
6685 // If the callee is a comptime-known *non-extern* function, `func_val` is populated.
6686 // If it is a comptime-known extern function, `func_is_extern` is set instead.
6687 // If it is not comptime-known, neither is set.
6688 const func_val: ?Value, const func_is_extern: bool = if (callee_val) |c| switch (ip.indexToKey(c.toIntern())) {
6689 .func => .{ c, false },
6690 .ptr => switch (try sema.pointerDerefExtra(block, func_src, c)) {
6691 .runtime_load, .needed_well_defined, .out_of_bounds => .{ null, false },
6692 .val => |pointee| switch (ip.indexToKey(pointee.toIntern())) {
6693 .func => .{ pointee, false },
6694 .@"extern" => .{ null, true },
6695 else => unreachable,
6696 },
6697 },
6698 .@"extern" => .{ null, true },
6699 else => unreachable,
6700 } else .{ null, false };
6701
6702 if ((any_generic_types or any_comptime_params) and func_val == null) {
6703 return sema.failWithNeededComptime(block, func_src, .{ .simple = .generic_call_target });
6704 }
6705
6706 const inline_requested = func_ty_info.cc == .@"inline" or modifier == .always_inline;
6707
6708 // If the modifier is `.compile_time`, or if the return type is non-generic and comptime-only,
6709 // then we need to enter a comptime scope *now* to make sure the args are comptime-eval'd.
6710 const old_block_comptime_reason = block.comptime_reason;
6711 defer block.comptime_reason = old_block_comptime_reason;
6712 if (!block.isComptime()) {
6713 if (modifier == .compile_time) {
6714 block.comptime_reason = .{ .reason = .{
6715 .src = call_src,
6716 .r = .{ .simple = .comptime_call_modifier },
6717 } };
6718 } else if (!inline_requested) {
6719 const ret_ty: Type = .fromInterned(func_ty_info.return_type);
6720 if (ret_ty.comptimeOnly(zcu)) {
6721 block.comptime_reason = .{ .reason = .{
6722 .src = call_src,
6723 .r = .{ .comptime_only_ret_ty = .{
6724 .ty = .fromInterned(func_ty_info.return_type),
6725 .is_generic_inst = false,
6726 .ret_ty_src = func_ret_ty_src,
6727 } },
6728 } };
6729 }
6730 }
6731 }
6732
6733 // This is whether we already know this to be an inline call.
6734 // If so, then comptime-known arguments are propagated when evaluating generic parameter/return types.
6735 // We might still learn that this call is inline *after* evaluating the generic return type.
6736 const early_known_inline = inline_requested or block.isComptime();
6737
6738 // These values are undefined if `func_val == null`.
6739 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: {
6740 const info = ip.indexToKey(f.toIntern()).func;
6741 const nav = ip.getNav(info.owner_nav);
6742 const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse {
6743 return sema.failTransitive(.{ .lost_tracking = info.zir_body_inst });
6744 };
6745 const file = zcu.fileByIndex(resolved_func_inst.file);
6746 const zir_info = file.zir.?.getFnInfo(resolved_func_inst.inst);
6747 break :b .{ nav, file.zir.?, info.zir_body_inst, resolved_func_inst.inst, zir_info };
6748 } else .{ undefined, undefined, undefined, undefined, undefined };
6749
6750 // This is the `inst_map` used when evaluating generic parameters and return types.
6751 var generic_inst_map: InstMap = .{};
6752 defer generic_inst_map.deinit(gpa);
6753 if (any_generic_types) {
6754 try generic_inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body);
6755 }
6756
6757 // This exists so that `generic_block` below can include a "called from here" note back to this
6758 // call site when analyzing generic parameter/return types.
6759 var generic_inlining: Block.Inlining = if (any_generic_types) .{
6760 .call_block = block,
6761 .call_src = call_src,
6762 .func = func_val.?.toIntern(),
6763 .is_generic_instantiation = true, // this allows the following fields to be `undefined`
6764 .has_comptime_args = undefined,
6765 .comptime_result = undefined,
6766 .merges = undefined,
6767 } else undefined;
6768
6769 // This is the block in which we evaluate generic function components: that is, generic parameter
6770 // types and the generic return type. This must not be used if the function is not generic.
6771 // `comptime_reason` is set as needed.
6772 var generic_block: Block = if (any_generic_types) .{
6773 .parent = null,
6774 .sema = sema,
6775 .namespace = fn_nav.analysis.?.namespace,
6776 .instructions = .empty,
6777 .inlining = &generic_inlining,
6778 .src_base_inst = fn_nav.analysis.?.zir_index,
6779 .type_name_ctx = fn_nav.name,
6780 .type_fqn_ctx = fn_nav.fqn,
6781 } else undefined;
6782 defer if (any_generic_types) generic_block.instructions.deinit(gpa);
6783
6784 if (any_generic_types) {
6785 // We certainly depend on the generic owner's signature!
6786 try sema.declareDependency(.{ .src_hash = fn_tracked_inst });
6787 }
6788
6789 const args = try arena.alloc(Air.Inst.Ref, args_info.count());
6790 for (args, 0..) |*arg, arg_idx| {
6791 const param_ty: ?Type = if (arg_idx < func_ty_info.param_types.len) ty: {
6792 const raw = func_ty_info.param_types.get(ip)[arg_idx];
6793 if (raw != .generic_poison_type) break :ty .fromInterned(raw);
6794
6795 // We must discover the generic parameter type.
6796 assert(any_generic_types);
6797 const param_inst_idx = fn_zir_info.param_body[arg_idx];
6798 const param_inst = fn_zir.instructions.get(@backingInt(param_inst_idx));
6799 switch (param_inst.tag) {
6800 .param_anytype, .param_anytype_comptime => break :ty .generic_poison,
6801 .param, .param_comptime => {},
6802 else => unreachable,
6803 }
6804
6805 // Evaluate the generic parameter type. We need to switch out `sema.code` and `sema.inst_map`, because
6806 // the function definition may be in a different file to the call site.
6807 const old_code = sema.code;
6808 const old_inst_map = sema.inst_map;
6809 defer {
6810 generic_inst_map = sema.inst_map;
6811 sema.code = old_code;
6812 sema.inst_map = old_inst_map;
6813 }
6814 sema.code = fn_zir;
6815 sema.inst_map = generic_inst_map;
6816
6817 const extra = sema.code.extraData(Zir.Inst.Param, param_inst.data.pl_tok.payload_index);
6818 const param_src = generic_block.tokenOffset(param_inst.data.pl_tok.src_tok);
6819 const body = sema.code.bodySlice(extra.end, extra.data.type.body_len);
6820
6821 generic_block.comptime_reason = .{ .reason = .{
6822 .r = .{ .simple = .fn_param_types },
6823 .src = param_src,
6824 } };
6825
6826 const ty_ref = try sema.resolveInlineBody(&generic_block, body, param_inst_idx);
6827 const param_ty = try sema.analyzeAsType(&generic_block, param_src, .fn_param_types, ty_ref);
6828
6829 if (!param_ty.isValidParamType(zcu)) {
6830 const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
6831 return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{
6832 opaque_str, param_ty.fmt(pt),
6833 });
6834 }
6835
6836 break :ty param_ty;
6837 } else null; // vararg
6838
6839 arg.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, callee, maybe_func_inst);
6840 const arg_ty = sema.typeOf(arg.*);
6841 if (arg_ty.classify(zcu) == .no_possible_value) {
6842 return arg.*; // terminate analysis here
6843 }
6844
6845 if (any_generic_types) {
6846 // We need to put the argument into `generic_inst_map` so that other parameters can refer to it.
6847 const param_inst_idx = fn_zir_info.param_body[arg_idx];
6848 const declared_comptime = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsComptime(i) else false;
6849 const param_is_comptime = declared_comptime or arg_ty.comptimeOnly(zcu);
6850 // We allow comptime-known arguments to propagate to generic types not only for comptime
6851 // parameters, but if the call is known to be inline.
6852 if (param_is_comptime or early_known_inline) {
6853 if (param_is_comptime and !try sema.isComptimeKnown(arg.*)) {
6854 assert(!declared_comptime); // `analyzeArg` handles this
6855 const arg_src = args_info.argSrc(block, arg_idx);
6856 const param_ty_src: LazySrcLoc = .{
6857 .base_node_inst = maybe_func_inst.?, // the function is generic
6858 .offset = .{ .func_decl_param_ty = @intCast(arg_idx) },
6859 };
6860 return sema.failWithNeededComptime(
6861 block,
6862 arg_src,
6863 .{ .comptime_only_param_ty = .{ .ty = arg_ty, .param_ty_src = param_ty_src } },
6864 );
6865 }
6866 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, arg.*);
6867 } else if (try arg_ty.onePossibleValue(pt)) |opv| {
6868 // The argument is comptime-known, even though this is a generic instantiation (as
6869 // opposed to an inline call), because the parameter type is OPV.
6870 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, .fromValue(opv));
6871 } else {
6872 // We need a dummy instruction with this type. It doesn't actually need to be in any block,
6873 // since it will never be referenced at runtime!
6874 const dummy: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
6875 try sema.air_instructions.append(gpa, .{ .tag = .alloc, .data = .{ .ty = arg_ty } });
6876 generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, dummy.toRef());
6877 }
6878 }
6879 }
6880
6881 // This return type is never generic poison.
6882 // However, if it has an IES, it is always associated with the callee value.
6883 // This is not correct for inline calls (where it should be an ad-hoc IES), nor for generic
6884 // calls (where it should be the IES of the instantiation). However, it's how we print this
6885 // in error messages.
6886 const resolved_ret_ty: Type = ret_ty: {
6887 if (!any_generic_types) break :ret_ty .fromInterned(func_ty_info.return_type);
6888
6889 const maybe_poison_bare = if (fn_zir_info.inferred_error_set) maybe_poison: {
6890 break :maybe_poison ip.errorUnionPayload(func_ty_info.return_type);
6891 } else func_ty_info.return_type;
6892
6893 if (maybe_poison_bare != .generic_poison_type) break :ret_ty .fromInterned(func_ty_info.return_type);
6894
6895 // Evaluate the generic return type. As with generic parameters, we switch out `sema.code` and `sema.inst_map`.
6896
6897 assert(any_generic_types);
6898
6899 const old_code = sema.code;
6900 const old_inst_map = sema.inst_map;
6901 defer {
6902 generic_inst_map = sema.inst_map;
6903 sema.code = old_code;
6904 sema.inst_map = old_inst_map;
6905 }
6906 sema.code = fn_zir;
6907 sema.inst_map = generic_inst_map;
6908
6909 generic_block.comptime_reason = .{ .reason = .{
6910 .r = .{ .simple = .fn_ret_ty },
6911 .src = func_ret_ty_src,
6912 } };
6913
6914 const bare_ty = if (fn_zir_info.ret_ty_ref != .none) bare: {
6915 assert(fn_zir_info.ret_ty_body.len == 0);
6916 break :bare try sema.resolveType(&generic_block, func_ret_ty_src, fn_zir_info.ret_ty_ref);
6917 } else bare: {
6918 assert(fn_zir_info.ret_ty_body.len != 0);
6919 const ty_ref = try sema.resolveInlineBody(&generic_block, fn_zir_info.ret_ty_body, fn_zir_inst);
6920 break :bare try sema.analyzeAsType(&generic_block, func_ret_ty_src, .fn_ret_ty, ty_ref);
6921 };
6922 assert(bare_ty.toIntern() != .generic_poison_type);
6923
6924 const full_ty = if (fn_zir_info.inferred_error_set) full: {
6925 try sema.validateErrorUnionPayloadType(block, bare_ty, func_ret_ty_src);
6926 const set = ip.errorUnionSet(func_ty_info.return_type);
6927 break :full try pt.errorUnionType(.fromInterned(set), bare_ty);
6928 } else bare_ty;
6929
6930 if (!full_ty.isValidReturnType(zcu)) {
6931 const opaque_str = if (full_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
6932 return sema.fail(block, func_ret_ty_src, "{s}return type '{f}' not allowed", .{
6933 opaque_str, full_ty.fmt(pt),
6934 });
6935 }
6936
6937 break :ret_ty full_ty;
6938 };
6939 try sema.ensureLayoutResolved(resolved_ret_ty, func_ret_ty_src, .return_type);
6940
6941 // If we've discovered after evaluating arguments that a generic function instantiation is
6942 // comptime-only, then we can mark the block as comptime *now*.
6943 if (!inline_requested and !block.isComptime() and resolved_ret_ty.comptimeOnly(zcu)) {
6944 block.comptime_reason = .{
6945 .reason = .{
6946 .src = call_src,
6947 .r = .{
6948 .comptime_only_ret_ty = .{
6949 .ty = resolved_ret_ty,
6950 .is_generic_inst = true,
6951 .ret_ty_src = func_ret_ty_src,
6952 },
6953 },
6954 },
6955 };
6956 }
6957
6958 if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
6959
6960 const is_inline_call = block.isComptime() or inline_requested;
6961
6962 if (!is_inline_call) {
6963 if (func_val == null and !func_is_extern and !block.is_typeof and zcu.getTarget().cpu.arch.isSpirV()) {
6964 return sema.fail(block, func_src, "SPIR-V does not support calling function pointers", .{});
6965 }
6966 if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: {
6967 const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)});
6968 errdefer msg.destroy(gpa);
6969 switch (operation) {
6970 .call, .@"@call", .@"@panic", .@"error return" => {},
6971 .@"safety check" => try sema.errNote(call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}),
6972 }
6973 break :msg msg;
6974 });
6975 if (func_ty_info.cc == .auto) {
6976 switch (sema.owner.unwrap()) {
6977 .@"comptime",
6978 .nav_ty,
6979 .nav_val,
6980 .type_layout,
6981 .struct_defaults,
6982 .memoized_state,
6983 => {},
6984
6985 .func => |owner_func| ip.funcSetHasErrorTrace(io, owner_func, true),
6986 }
6987 }
6988 for (args, 0..) |arg, arg_idx| {
6989 const arg_src = args_info.argSrc(block, arg_idx);
6990 try sema.validateRuntimeValue(block, arg_src, arg);
6991 }
6992 const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: {
6993 if (!any_generic_types and !any_comptime_params) break :func .{ callee, args };
6994
6995 // Instantiate the generic function!
6996
6997 // This may be an overestimate, but it's definitely sufficient.
6998 const max_runtime_args = args_info.count() - @popCount(func_ty_info.comptime_bits);
6999 var runtime_args: std.ArrayList(Air.Inst.Ref) = try .initCapacity(arena, max_runtime_args);
7000 var runtime_param_tys: std.ArrayList(InternPool.Index) = try .initCapacity(arena, max_runtime_args);
7001
7002 const comptime_args = try arena.alloc(InternPool.Index, args_info.count());
7003
7004 var noalias_bits: u32 = 0;
7005
7006 for (args, comptime_args, 0..) |arg, *comptime_arg, arg_idx| {
7007 const arg_ty = sema.typeOf(arg);
7008
7009 const is_comptime = c: {
7010 if (std.math.cast(u5, arg_idx)) |i| {
7011 if (func_ty_info.paramIsComptime(i)) {
7012 break :c true;
7013 }
7014 }
7015 break :c arg_ty.comptimeOnly(zcu);
7016 };
7017 const is_noalias = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsNoalias(i) else false;
7018
7019 if (is_comptime) {
7020 // We already emitted an error if the argument isn't comptime-known.
7021 comptime_arg.* = sema.resolveValue(arg).?.toIntern();
7022 } else {
7023 comptime_arg.* = .none;
7024 if (is_noalias) {
7025 const runtime_idx = runtime_args.items.len;
7026 noalias_bits |= @as(u32, 1) << @intCast(runtime_idx);
7027 }
7028 runtime_args.appendAssumeCapacity(arg);
7029 runtime_param_tys.appendAssumeCapacity(arg_ty.toIntern());
7030 }
7031 }
7032
7033 const bare_ret_ty = if (fn_zir_info.inferred_error_set) t: {
7034 break :t resolved_ret_ty.errorUnionPayload(zcu);
7035 } else resolved_ret_ty;
7036
7037 // We now need to actually create the function instance.
7038 const func_instance = try ip.getFuncInstance(gpa, io, pt.tid, .{
7039 .param_types = runtime_param_tys.items,
7040 .noalias_bits = noalias_bits,
7041 .bare_return_type = bare_ret_ty.toIntern(),
7042 .is_noinline = func_ty_info.is_noinline,
7043 .inferred_error_set = fn_zir_info.inferred_error_set,
7044 .generic_owner = func_val.?.toIntern(),
7045 .comptime_args = comptime_args,
7046 .anon_name_counter = &zcu.anon_name_counter,
7047 });
7048 if (zcu.comp.debugIncremental()) {
7049 const nav = ip.indexToKey(func_instance).func.owner_nav;
7050 const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav);
7051 if (!gop.found_existing) gop.value_ptr.* = zcu.generation;
7052 }
7053
7054 // This call is problematic as it breaks guarantees about order-independency of semantic analysis.
7055 // These guarantees are necessary for incremental compilation and parallel semantic analysis.
7056 // See: #22410
7057 zcu.funcInfo(func_instance).maxBranchQuota(ip, io, sema.branch_quota);
7058
7059 break :func .{ Air.internedToRef(func_instance), runtime_args.items };
7060 };
7061
7062 ref_func: {
7063 const runtime_func_val = sema.resolveValue(runtime_func) orelse break :ref_func;
7064 if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func;
7065 const orig_fn_index = ip.unwrapCoercedFunc(runtime_func_val.toIntern());
7066 try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = orig_fn_index }));
7067 try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
7068 }
7069
7070 const call_tag: Air.Inst.Tag = switch (modifier) {
7071 .auto, .no_suspend => .call,
7072 .never_tail => .call_never_tail,
7073 .never_inline => .call_never_inline,
7074 .always_tail => .call_always_tail,
7075
7076 .always_inline,
7077 .compile_time,
7078 => unreachable,
7079 };
7080
7081 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".field_names.len + runtime_args.len);
7082 const call_ref = try block.addInst(.{
7083 .tag = call_tag,
7084 .data = .{ .pl_op = .{
7085 .operand = runtime_func,
7086 .payload = sema.addExtraAssumeCapacity(Air.Call{
7087 .args_len = @intCast(runtime_args.len),
7088 }),
7089 } },
7090 });
7091 sema.appendRefsAssumeCapacity(runtime_args);
7092
7093 const actual_ret_ty = sema.typeOf(call_ref);
7094
7095 if (ensure_result_used) {
7096 try sema.ensureResultUsed(block, actual_ret_ty, call_src);
7097 }
7098
7099 if (call_tag == .call_always_tail) {
7100 const func_or_ptr_ty = sema.typeOf(runtime_func);
7101 const runtime_func_ty = switch (func_or_ptr_ty.zigTypeTag(zcu)) {
7102 .@"fn" => func_or_ptr_ty,
7103 .pointer => func_or_ptr_ty.childType(zcu),
7104 else => unreachable,
7105 };
7106 const result = sema.coerceExtra(block, sema.fn_ret_ty, call_ref, call_src, .{ .is_ret = true }) catch |err| switch (err) {
7107 error.NotCoercible => unreachable,
7108 else => |e| return e,
7109 };
7110 return sema.handleTailCall(block, call_src, runtime_func_ty, result);
7111 }
7112
7113 switch (actual_ret_ty.classify(zcu)) {
7114 .no_possible_value => {
7115 const want_check = c: {
7116 if (!block.wantSafety()) break :c false;
7117 if (func_val != null) break :c false;
7118 break :c true;
7119 };
7120 if (want_check) {
7121 try sema.safetyPanic(block, call_src, .noreturn_returned);
7122 } else {
7123 _ = try block.addNoOp(.unreach);
7124 }
7125 return .unreachable_value;
7126 },
7127 .one_possible_value => {
7128 return .fromValue((try actual_ret_ty.onePossibleValue(pt)).?);
7129 },
7130 .runtime => {
7131 return call_ref;
7132 },
7133 .partially_comptime => unreachable,
7134 .fully_comptime => unreachable,
7135 }
7136 }
7137
7138 // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`.
7139
7140 if (zcu.comp.time_report) |*tr| {
7141 if (!block.isComptime()) {
7142 tr.stats.n_inline_calls += 1;
7143 }
7144 }
7145
7146 if (func_ty_info.is_noinline and !block.isComptime()) {
7147 return sema.fail(block, call_src, "inline call of noinline function", .{});
7148 }
7149
7150 const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline";
7151 if (modifier == .never_inline) {
7152 const msg, const fail_block = msg: {
7153 const msg = try sema.errMsg(call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type});
7154 errdefer msg.destroy(gpa);
7155 const fail_block = if (block.isComptime()) b: {
7156 break :b try block.explainWhyBlockIsComptime(msg);
7157 } else block;
7158 break :msg .{ msg, fail_block };
7159 };
7160 return sema.failWithOwnedErrorMsg(fail_block, msg);
7161 }
7162 if (func_ty_info.is_var_args) {
7163 const msg, const fail_block = msg: {
7164 const msg = try sema.errMsg(call_src, "{s} call of variadic function", .{call_type});
7165 errdefer msg.destroy(gpa);
7166 const fail_block = if (block.isComptime()) b: {
7167 break :b try block.explainWhyBlockIsComptime(msg);
7168 } else block;
7169 break :msg .{ msg, fail_block };
7170 };
7171 return sema.failWithOwnedErrorMsg(fail_block, msg);
7172 }
7173 if (func_val == null) {
7174 if (func_is_extern) {
7175 const msg, const fail_block = msg: {
7176 const msg = try sema.errMsg(call_src, "{s} call of extern function", .{call_type});
7177 errdefer msg.destroy(gpa);
7178 const fail_block = if (block.isComptime()) b: {
7179 break :b try block.explainWhyBlockIsComptime(msg);
7180 } else block;
7181 break :msg .{ msg, fail_block };
7182 };
7183 return sema.failWithOwnedErrorMsg(fail_block, msg);
7184 }
7185 return sema.failWithNeededComptime(
7186 block,
7187 func_src,
7188 if (block.isComptime()) null else .{ .simple = .inline_call_target },
7189 );
7190 }
7191
7192 if (block.isComptime()) {
7193 for (args, 0..) |arg, arg_idx| {
7194 if (!try sema.isComptimeKnown(arg)) {
7195 const arg_src = args_info.argSrc(block, arg_idx);
7196 return sema.failWithNeededComptime(block, arg_src, null);
7197 }
7198 }
7199 }
7200
7201 // For an inline call, we depend on the source code of the whole function definition.
7202 try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index });
7203
7204 try sema.emitBackwardBranch(block, call_src);
7205
7206 const want_memoize = m: {
7207 // TODO: comptime call memoization is currently not supported under incremental compilation
7208 // since dependencies are not marked on callers. If we want to keep this around (we should
7209 // check that it's worthwhile first!), each memoized call needs an `AnalUnit`.
7210 if (zcu.comp.config.incremental) break :m false;
7211 if (!block.isComptime()) break :m false;
7212 for (args) |a| {
7213 const val = sema.resolveValue(a).?;
7214 if (val.canMutateComptimeVarState(zcu)) break :m false;
7215 }
7216 break :m true;
7217 };
7218 const memoized_arg_values: []const InternPool.Index = if (want_memoize) arg_vals: {
7219 const vals = try sema.arena.alloc(InternPool.Index, args.len);
7220 for (vals, args) |*v, a| v.* = sema.resolveValue(a).?.toIntern();
7221 break :arg_vals vals;
7222 } else undefined;
7223 if (want_memoize) memoize: {
7224 const memoized_call_index = ip.getIfExists(.{
7225 .memoized_call = .{
7226 .func = func_val.?.toIntern(),
7227 .arg_values = memoized_arg_values,
7228 .result = undefined, // ignored by hash+eql
7229 .branch_count = undefined, // ignored by hash+eql
7230 .branch_quota = undefined, // ignored by hash+eql
7231 },
7232 }) orelse break :memoize;
7233 const memoized_call = ip.indexToKey(memoized_call_index).memoized_call;
7234 if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) {
7235 // Let the call play out se we get the correct source location for the
7236 // "evaluation exceeded X backwards branches" error.
7237 break :memoize;
7238 }
7239 sema.branch_count += memoized_call.branch_count;
7240 sema.branch_quota = @max(sema.branch_quota, memoized_call.branch_quota);
7241 sema.quota_request = @max(sema.quota_request, memoized_call.branch_quota);
7242 const result = Air.internedToRef(memoized_call.result);
7243 if (ensure_result_used) {
7244 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
7245 }
7246 return result;
7247 }
7248
7249 var new_ies: InferredErrorSet = .{ .func = .none };
7250
7251 const old_inst_map = sema.inst_map;
7252 const old_code = sema.code;
7253 const old_func_index = sema.func_index;
7254 const old_fn_ret_ty = sema.fn_ret_ty;
7255 const old_fn_ret_ty_ies = sema.fn_ret_ty_ies;
7256 const old_error_return_trace_index_on_fn_entry = sema.error_return_trace_index_on_fn_entry;
7257 defer {
7258 sema.inst_map.deinit(gpa);
7259 sema.inst_map = old_inst_map;
7260 sema.code = old_code;
7261 sema.func_index = old_func_index;
7262 sema.fn_ret_ty = old_fn_ret_ty;
7263 sema.fn_ret_ty_ies = old_fn_ret_ty_ies;
7264 sema.error_return_trace_index_on_fn_entry = old_error_return_trace_index_on_fn_entry;
7265 }
7266 sema.inst_map = .{};
7267 sema.code = fn_zir;
7268 sema.func_index = func_val.?.toIntern();
7269 sema.fn_ret_ty = if (fn_zir_info.inferred_error_set) try pt.errorUnionType(
7270 .fromInterned(.adhoc_inferred_error_set_type),
7271 resolved_ret_ty.errorUnionPayload(zcu),
7272 ) else resolved_ret_ty;
7273 sema.fn_ret_ty_ies = if (fn_zir_info.inferred_error_set) &new_ies else null;
7274
7275 try sema.inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body);
7276 for (args, 0..) |arg, arg_idx| {
7277 sema.inst_map.putAssumeCapacityNoClobber(fn_zir_info.param_body[arg_idx], arg);
7278 }
7279
7280 const need_debug_scope = !block.isComptime() and !block.is_typeof and !block.ownerModule().strip;
7281 const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
7282 try sema.air_instructions.append(gpa, .{
7283 .tag = if (need_debug_scope) .dbg_inline_block else .block,
7284 .data = undefined,
7285 });
7286
7287 var inlining: Block.Inlining = .{
7288 .call_block = block,
7289 .call_src = call_src,
7290 .func = func_val.?.toIntern(),
7291 .is_generic_instantiation = false,
7292 .has_comptime_args = for (args) |a| {
7293 if (try sema.isComptimeKnown(a)) break true;
7294 } else false,
7295 .comptime_result = undefined,
7296 .merges = .{
7297 .block_inst = block_inst,
7298 .results = .empty,
7299 .br_list = .empty,
7300 .src_locs = .empty,
7301 },
7302 };
7303 var child_block: Block = .{
7304 .parent = null,
7305 .sema = sema,
7306 .namespace = fn_nav.analysis.?.namespace,
7307 .instructions = .empty,
7308 .inlining = &inlining,
7309 .is_typeof = block.is_typeof,
7310 .comptime_reason = if (block.isComptime()) .inlining_parent else null,
7311 .error_return_trace_index = block.error_return_trace_index,
7312 .runtime_cond = block.runtime_cond,
7313 .runtime_loop = block.runtime_loop,
7314 .runtime_index = block.runtime_index,
7315 .src_base_inst = fn_nav.analysis.?.zir_index,
7316 .type_name_ctx = fn_nav.name,
7317 .type_fqn_ctx = fn_nav.fqn,
7318 };
7319
7320 defer child_block.instructions.deinit(gpa);
7321 defer inlining.merges.deinit(gpa);
7322
7323 if (!inlining.has_comptime_args) {
7324 var block_it = block;
7325 while (block_it.inlining) |parent_inlining| {
7326 if (!parent_inlining.is_generic_instantiation and
7327 !parent_inlining.has_comptime_args and
7328 parent_inlining.func == func_val.?.toIntern())
7329 {
7330 return sema.fail(block, call_src, "inline call is recursive", .{});
7331 }
7332 block_it = parent_inlining.call_block;
7333 }
7334 }
7335
7336 if (!block.isComptime() and !block.is_typeof) {
7337 const zir_tags = sema.code.instructions.items(.tag);
7338 const zir_datas = sema.code.instructions.items(.data);
7339 for (fn_zir_info.param_body) |inst| switch (zir_tags[@backingInt(inst)]) {
7340 .param, .param_comptime => {
7341 const extra = sema.code.extraData(Zir.Inst.Param, zir_datas[@backingInt(inst)].pl_tok.payload_index);
7342 const param_name = sema.code.nullTerminatedString(extra.data.name);
7343 const air_inst = sema.inst_map.get(inst).?;
7344 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name);
7345 },
7346 .param_anytype, .param_anytype_comptime => {
7347 const param_name = zir_datas[@backingInt(inst)].str_tok.get(sema.code);
7348 const air_inst = sema.inst_map.get(inst).?;
7349 try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name);
7350 },
7351 else => {},
7352 };
7353 }
7354
7355 child_block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block);
7356 // Save the error trace as our first action in the function
7357 // to match the behavior of runtime function calls.
7358 const error_return_trace_index_on_parent_fn_entry = sema.error_return_trace_index_on_fn_entry;
7359 sema.error_return_trace_index_on_fn_entry = child_block.error_return_trace_index;
7360 defer sema.error_return_trace_index_on_fn_entry = error_return_trace_index_on_parent_fn_entry;
7361
7362 // We temporarily set `allow_memoize` to `true` to track this comptime call.
7363 // It is restored after the call finishes analysis, so that a caller may
7364 // know whether an in-progress call (containing this call) may be memoized.
7365 const old_allow_memoize = sema.allow_memoize;
7366 defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize;
7367 sema.allow_memoize = true;
7368
7369 const old_quota_request = sema.quota_request;
7370 defer sema.quota_request = @max(old_quota_request, sema.quota_request);
7371 sema.quota_request = 0;
7372
7373 // Store the current eval branch count so we can find out how many eval branches
7374 // the comptime call caused.
7375 const old_branch_count = sema.branch_count;
7376
7377 const result_raw: Air.Inst.Ref = result: {
7378 sema.analyzeFnBody(&child_block, fn_zir_info.body) catch |err| switch (err) {
7379 error.ComptimeReturn => break :result inlining.comptime_result,
7380 else => |e| return e,
7381 };
7382 break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope);
7383 };
7384
7385 if (sema.typeOf(result_raw).isNoReturn(zcu)) {
7386 return .unreachable_value;
7387 }
7388
7389 const maybe_opv: Air.Inst.Ref = if (sema.resolveValue(result_raw)) |result_val| r: {
7390 const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern());
7391 break :r Air.internedToRef(val_resolved);
7392 } else r: {
7393 const resolved_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result_raw).toIntern());
7394 if (resolved_ty == .none) break :r result_raw;
7395 // TODO: mutate in place the previous instruction if possible
7396 // rather than adding a bitcast instruction.
7397 break :r try block.addTyOp(.error_cast, .fromInterned(resolved_ty), result_raw);
7398 };
7399
7400 if (block.isComptime()) {
7401 const result_val = sema.resolveValue(maybe_opv).?;
7402 if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) {
7403 _ = try pt.intern(.{ .memoized_call = .{
7404 .func = func_val.?.toIntern(),
7405 .arg_values = memoized_arg_values,
7406 .result = result_val.toIntern(),
7407 .branch_count = sema.branch_count - old_branch_count,
7408 .branch_quota = sema.quota_request,
7409 } });
7410 }
7411 }
7412
7413 if (ensure_result_used) {
7414 try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src);
7415 }
7416
7417 return maybe_opv;
7418}
7419
7420fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref {
7421 const pt = sema.pt;
7422 const zcu = pt.zcu;
7423 const target = zcu.getTarget();
7424 const backend = zcu.comp.getZigBackend();
7425 if (!target_util.supportsTailCall(target, backend)) {
7426 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", .{
7427 @tagName(backend), @tagName(target.cpu.arch),
7428 });
7429 }
7430 const owner_func_ty: Type = .fromInterned(zcu.funcInfo(sema.owner.unwrap().func).ty);
7431 if (owner_func_ty.toIntern() != func_ty.toIntern()) {
7432 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}'", .{
7433 func_ty.fmt(pt), owner_func_ty.fmt(pt),
7434 });
7435 }
7436 _ = try block.addUnOp(.ret, result);
7437 return .unreachable_value;
7438}
7439
7440fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7441 const int_type = sema.code.instructions.items(.data)[@backingInt(inst)].int_type;
7442 const ty = try sema.pt.intType(int_type.signedness, int_type.bit_count);
7443 return Air.internedToRef(ty.toIntern());
7444}
7445
7446fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7447 const pt = sema.pt;
7448 const zcu = pt.zcu;
7449 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
7450 const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
7451 const child_type = try sema.resolveType(block, operand_src, inst_data.operand);
7452 if (child_type.zigTypeTag(zcu) == .@"opaque") {
7453 return sema.fail(block, operand_src, "opaque type '{f}' cannot be optional", .{child_type.fmt(pt)});
7454 } else if (child_type.zigTypeTag(zcu) == .null) {
7455 return sema.fail(block, operand_src, "type '{f}' cannot be optional", .{child_type.fmt(pt)});
7456 }
7457 const opt_type = try pt.optionalType(child_type.toIntern());
7458
7459 return Air.internedToRef(opt_type.toIntern());
7460}
7461
7462fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7463 const pt = sema.pt;
7464 const zcu = pt.zcu;
7465 const bin = sema.code.instructions.items(.data)[@backingInt(inst)].bin;
7466 const maybe_wrapped_indexable_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, bin.lhs) orelse return .generic_poison_type;
7467 const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(zcu);
7468 assert(indexable_ty.isIndexable(zcu)); // validated by a previous instruction
7469 const elem_ty = switch (indexable_ty.zigTypeTag(zcu)) {
7470 .@"struct" => indexable_ty.fieldType(@backingInt(bin.rhs), zcu),
7471 else => indexable_ty.indexableElem(zcu),
7472 };
7473 return .fromType(elem_ty);
7474}
7475
7476fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7477 const pt = sema.pt;
7478 const zcu = pt.zcu;
7479 const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
7480 const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type;
7481 const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu);
7482 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
7483 const elem_ty = ptr_ty.childType(zcu);
7484 if (elem_ty.toIntern() == .anyopaque_type) {
7485 // The pointer's actual child type is effectively unknown, so it makes
7486 // sense to represent it with a generic poison.
7487 return .generic_poison_type;
7488 }
7489 return Air.internedToRef(ptr_ty.childType(zcu).toIntern());
7490}
7491
7492fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7493 const pt = sema.pt;
7494 const zcu = pt.zcu;
7495 const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
7496 const src = block.nodeOffset(un_node.src_node);
7497 const ptr_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type;
7498 try sema.checkMemOperand(block, src, ptr_ty);
7499 const elem_ty = switch (ptr_ty.ptrSize(zcu)) {
7500 .slice, .many, .c => ptr_ty.childType(zcu),
7501 .one => ptr_ty.childType(zcu).childType(zcu),
7502 };
7503 return Air.internedToRef(elem_ty.toIntern());
7504}
7505
7506fn zirSplatOpResultType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7507 const pt = sema.pt;
7508 const zcu = pt.zcu;
7509 const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
7510
7511 const raw_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type;
7512 const vec_ty = raw_ty.optEuBaseType(zcu);
7513
7514 switch (vec_ty.zigTypeTag(zcu)) {
7515 .array, .vector => {},
7516 else => return sema.fail(block, block.nodeOffset(un_node.src_node), "expected array or vector type, found '{f}'", .{vec_ty.fmt(pt)}),
7517 }
7518 return Air.internedToRef(vec_ty.childType(zcu).toIntern());
7519}
7520
7521fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7522 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
7523 const len_src = block.builtinCallArgSrc(inst_data.src_node, 0);
7524 const elem_type_src = block.builtinCallArgSrc(inst_data.src_node, 1);
7525 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
7526 const len: u32 = @intCast(try sema.resolveInt(block, len_src, extra.lhs, .u32, .{ .simple = .vector_length }));
7527 const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs);
7528 try sema.checkVectorElemType(block, elem_type_src, elem_type);
7529 const vector_type = try sema.pt.vectorType(.{
7530 .len = len,
7531 .child = elem_type.toIntern(),
7532 });
7533 return Air.internedToRef(vector_type.toIntern());
7534}
7535
7536fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7537 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
7538 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
7539 const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node });
7540 const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node });
7541 const len = try sema.resolveInt(block, len_src, extra.lhs, .usize, .{ .simple = .array_length });
7542 const elem_type = try sema.resolveType(block, elem_src, extra.rhs);
7543 try sema.validateArrayElemType(block, elem_type, elem_src);
7544 const array_ty = try sema.pt.arrayType(.{
7545 .len = len,
7546 .child = elem_type.toIntern(),
7547 });
7548
7549 return Air.internedToRef(array_ty.toIntern());
7550}
7551
7552fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7553 const pt = sema.pt;
7554 const zcu = pt.zcu;
7555 const comp = zcu.comp;
7556 const gpa = comp.gpa;
7557 const io = comp.io;
7558 const ip = &zcu.intern_pool;
7559
7560 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
7561 const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
7562 const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node });
7563 const sentinel_src = block.src(.{ .node_offset_array_type_sentinel = inst_data.src_node });
7564 const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node });
7565 const len = try sema.resolveInt(block, len_src, extra.len, .usize, .{ .simple = .array_length });
7566 const elem_type = try sema.resolveType(block, elem_src, extra.elem_type);
7567 try sema.validateArrayElemType(block, elem_type, elem_src);
7568 const uncasted_sentinel = sema.resolveInst(extra.sentinel);
7569 const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src);
7570 const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{ .simple = .array_sentinel });
7571 if (sentinel_val.canMutateComptimeVarState(zcu)) {
7572 const sentinel_name = try ip.getOrPutString(gpa, io, pt.tid, "sentinel", .no_embedded_nulls);
7573 return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", sentinel_val);
7574 }
7575 const array_ty = try pt.arrayType(.{
7576 .len = len,
7577 .sentinel = sentinel_val.toIntern(),
7578 .child = elem_type.toIntern(),
7579 });
7580 try sema.checkSentinelType(block, sentinel_src, elem_type);
7581
7582 return Air.internedToRef(array_ty.toIntern());
7583}
7584
7585fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void {
7586 const pt = sema.pt;
7587 const zcu = pt.zcu;
7588 if (elem_type.zigTypeTag(zcu) == .@"opaque") {
7589 return sema.fail(block, elem_src, "array of opaque type '{f}' not allowed", .{elem_type.fmt(pt)});
7590 } else if (elem_type.zigTypeTag(zcu) == .noreturn) {
7591 return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{});
7592 }
7593}
7594
7595fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7596 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
7597 if (true) {
7598 return sema.failWithUseOfAsync(block, block.nodeOffset(inst_data.src_node));
7599 }
7600 const zcu = sema.zcu;
7601 const operand_src = block.src(.{ .node_offset_anyframe_type = inst_data.src_node });
7602 const return_type = try sema.resolveType(block, operand_src, inst_data.operand);
7603 const anyframe_type = try zcu.anyframeType(return_type);
7604
7605 return Air.internedToRef(anyframe_type.toIntern());
7606}
7607
7608fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7609 const pt = sema.pt;
7610 const zcu = pt.zcu;
7611 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
7612 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
7613 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
7614 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
7615 const error_set = try sema.resolveType(block, lhs_src, extra.lhs);
7616 const payload = try sema.resolveType(block, rhs_src, extra.rhs);
7617
7618 if (error_set.zigTypeTag(zcu) != .error_set) {
7619 return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{
7620 error_set.fmt(pt),
7621 });
7622 }
7623 try sema.validateErrorUnionPayloadType(block, payload, rhs_src);
7624 const err_union_ty = try pt.errorUnionType(error_set, payload);
7625 return Air.internedToRef(err_union_ty.toIntern());
7626}
7627
7628fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void {
7629 const pt = sema.pt;
7630 const zcu = pt.zcu;
7631 if (payload_ty.zigTypeTag(zcu) == .@"opaque") {
7632 return sema.fail(block, payload_src, "error union with payload of opaque type '{f}' not allowed", .{
7633 payload_ty.fmt(pt),
7634 });
7635 } else if (payload_ty.zigTypeTag(zcu) == .error_set) {
7636 return sema.fail(block, payload_src, "error union with payload of error set type '{f}' not allowed", .{
7637 payload_ty.fmt(pt),
7638 });
7639 }
7640}
7641
7642fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7643 _ = block;
7644
7645 const pt = sema.pt;
7646 const zcu = pt.zcu;
7647 const comp = zcu.comp;
7648 const gpa = comp.gpa;
7649 const io = comp.io;
7650
7651 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok;
7652 const name = try pt.zcu.intern_pool.getOrPutString(
7653 gpa,
7654 io,
7655 pt.tid,
7656 inst_data.get(sema.code),
7657 .no_embedded_nulls,
7658 );
7659 _ = try pt.getErrorValue(name);
7660 // Create an error set type with only this error value, and return the value.
7661 const error_set_type = try pt.singleErrorSetType(name);
7662 return Air.internedToRef((try pt.intern(.{ .err = .{
7663 .ty = error_set_type.toIntern(),
7664 .name = name,
7665 } })));
7666}
7667
7668fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
7669 const pt = sema.pt;
7670 const zcu = pt.zcu;
7671 const ip = &zcu.intern_pool;
7672 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
7673 const src = block.nodeOffset(extra.node);
7674 const operand_src = block.builtinCallArgSrc(extra.node, 0);
7675 const uncasted_operand = sema.resolveInst(extra.operand);
7676 const operand = try sema.coerce(block, .anyerror, uncasted_operand, operand_src);
7677 const err_int_ty = try pt.errorIntType();
7678
7679 if (sema.resolveValue(operand)) |val| {
7680 if (val.isUndef(zcu)) {
7681 return pt.undefRef(err_int_ty);
7682 }
7683 const err_name = ip.indexToKey(val.toIntern()).err.name;
7684 return Air.internedToRef((try pt.intValue(
7685 err_int_ty,
7686 try pt.getErrorValue(err_name),
7687 )).toIntern());
7688 }
7689
7690 const op_ty = sema.typeOf(uncasted_operand);
7691 switch (try sema.resolveInferredErrorSetTy(block, src, op_ty.toIntern())) {
7692 .anyerror_type => {},
7693 else => |err_set_ty_index| {
7694 const names = ip.indexToKey(err_set_ty_index).error_set_type.names;
7695 switch (names.len) {
7696 0 => return Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()),
7697 1 => return pt.intRef(err_int_ty, ip.getErrorValueIfExists(names.get(ip)[0]).?),
7698 else => {},
7699 }
7700 },
7701 }
7702
7703 try sema.requireRuntimeBlock(block, src, operand_src);
7704 return block.addTyOp(.int_from_error, err_int_ty, operand);
7705}
7706
7707fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
7708 const pt = sema.pt;
7709 const zcu = pt.zcu;
7710 const io = zcu.comp.io;
7711 const ip = &zcu.intern_pool;
7712
7713 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
7714 const src = block.nodeOffset(extra.node);
7715 const operand_src = block.builtinCallArgSrc(extra.node, 0);
7716 const uncasted_operand = sema.resolveInst(extra.operand);
7717 const err_int_ty = try pt.errorIntType();
7718 const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src);
7719
7720 if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| {
7721 const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(zcu));
7722 if (int > len: {
7723 const mutate = &ip.global_error_set.mutate;
7724 mutate.map.mutex.lockUncancelable(io);
7725 defer mutate.map.mutex.unlock(io);
7726 break :len mutate.names.len;
7727 } or int == 0)
7728 return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int});
7729 return Air.internedToRef((try pt.intern(.{ .err = .{
7730 .ty = .anyerror_type,
7731 .name = ip.global_error_set.shared.names.acquire().view().items(.@"0")[int - 1],
7732 } })));
7733 }
7734 try sema.requireRuntimeBlock(block, src, operand_src);
7735 if (block.wantSafety()) {
7736 const is_lte_len = try block.addUnOp(.cmp_lte_errors_len, operand);
7737 const zero_val = Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern());
7738 const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val);
7739 const ok = try block.addBinOp(.bit_and, is_lte_len, is_non_zero);
7740 try sema.addSafetyCheck(block, src, ok, .invalid_error_code);
7741 }
7742 return block.addTyOp(.error_from_int, .anyerror, operand);
7743}
7744
7745fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7746 const pt = sema.pt;
7747 const zcu = pt.zcu;
7748 const ip = &zcu.intern_pool;
7749 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
7750 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
7751 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
7752 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
7753 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
7754 const lhs = sema.resolveInst(extra.lhs);
7755 const rhs = sema.resolveInst(extra.rhs);
7756 if (sema.typeOf(lhs).zigTypeTag(zcu) == .bool and sema.typeOf(rhs).zigTypeTag(zcu) == .bool) {
7757 const msg = msg: {
7758 const msg = try sema.errMsg(lhs_src, "expected error set type, found 'bool'", .{});
7759 errdefer msg.destroy(sema.gpa);
7760 try sema.errNote(src, msg, "'||' merges error sets; 'or' performs boolean OR", .{});
7761 break :msg msg;
7762 };
7763 return sema.failWithOwnedErrorMsg(block, msg);
7764 }
7765 const lhs_ty = try sema.analyzeAsType(block, lhs_src, .type, lhs);
7766 const rhs_ty = try sema.analyzeAsType(block, rhs_src, .type, rhs);
7767 if (lhs_ty.zigTypeTag(zcu) != .error_set)
7768 return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{lhs_ty.fmt(pt)});
7769 if (rhs_ty.zigTypeTag(zcu) != .error_set)
7770 return sema.fail(block, rhs_src, "expected error set type, found '{f}'", .{rhs_ty.fmt(pt)});
7771
7772 // Anything merged with anyerror is anyerror.
7773 if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) {
7774 return .anyerror_type;
7775 }
7776
7777 switch (ip.indexToKey(lhs_ty.toIntern())) {
7778 .inferred_error_set_type => |func_index| {
7779 try sema.ensureFuncIesResolved(block, src, func_index);
7780 if (ip.funcIesResolvedUnordered(func_index) == .anyerror_type) return .anyerror_type;
7781 },
7782 .error_set_type => {},
7783 else => unreachable,
7784 }
7785 switch (ip.indexToKey(rhs_ty.toIntern())) {
7786 .inferred_error_set_type => |func_index| {
7787 try sema.ensureFuncIesResolved(block, src, func_index);
7788 if (ip.funcIesResolvedUnordered(func_index) == .anyerror_type) return .anyerror_type;
7789 },
7790 .error_set_type => {},
7791 else => unreachable,
7792 }
7793
7794 const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty);
7795 return Air.internedToRef(err_set_ty.toIntern());
7796}
7797
7798fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7799 _ = block;
7800
7801 const pt = sema.pt;
7802 const zcu = pt.zcu;
7803 const comp = zcu.comp;
7804 const gpa = comp.gpa;
7805 const io = comp.io;
7806
7807 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok;
7808 const name = inst_data.get(sema.code);
7809 return Air.internedToRef((try pt.intern(.{
7810 .enum_literal = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls),
7811 })));
7812}
7813
7814fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: bool) CompileError!Air.Inst.Ref {
7815 const pt = sema.pt;
7816 const zcu = pt.zcu;
7817 const comp = zcu.comp;
7818 const gpa = comp.gpa;
7819 const io = comp.io;
7820
7821 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
7822 const src = block.nodeOffset(inst_data.src_node);
7823 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
7824 const name = try zcu.intern_pool.getOrPutString(
7825 gpa,
7826 io,
7827 pt.tid,
7828 sema.code.nullTerminatedString(extra.field_name_start),
7829 .no_embedded_nulls,
7830 );
7831 const orig_ty: Type = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse .generic_poison;
7832 return sema.analyzeDeclLiteral(block, src, name, orig_ty, do_coerce);
7833}
7834
7835fn analyzeDeclLiteral(
7836 sema: *Sema,
7837 block: *Block,
7838 src: LazySrcLoc,
7839 name: InternPool.NullTerminatedString,
7840 orig_ty: Type,
7841 do_coerce: bool,
7842) CompileError!Air.Inst.Ref {
7843 const pt = sema.pt;
7844 const zcu = pt.zcu;
7845
7846 const uncoerced_result = res: {
7847 if (orig_ty.toIntern() == .generic_poison_type) {
7848 // Treat this as a normal enum literal.
7849 break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
7850 }
7851
7852 var ty = orig_ty;
7853 while (true) switch (ty.zigTypeTag(zcu)) {
7854 .error_union => ty = ty.errorUnionPayload(zcu),
7855 .optional => ty = ty.optionalChild(zcu),
7856 .pointer => ty = if (ty.isSinglePointer(zcu)) ty.childType(zcu) else break,
7857 .enum_literal, .error_set => {
7858 // Treat this as a normal enum literal.
7859 break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name }));
7860 },
7861 else => break,
7862 };
7863
7864 break :res try sema.fieldVal(block, src, Air.internedToRef(ty.toIntern()), name, src);
7865 };
7866
7867 // Decl literals cannot lookup runtime `var`s.
7868 if (!try sema.isComptimeKnown(uncoerced_result)) {
7869 return sema.fail(block, src, "decl literal must be comptime-known", .{});
7870 }
7871
7872 if (do_coerce) {
7873 return sema.coerce(block, orig_ty, uncoerced_result, src);
7874 } else {
7875 return uncoerced_result;
7876 }
7877}
7878
7879fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7880 const pt = sema.pt;
7881 const zcu = pt.zcu;
7882 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
7883 const src = block.nodeOffset(inst_data.src_node);
7884 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
7885 const operand = sema.resolveInst(inst_data.operand);
7886 const operand_ty = sema.typeOf(operand);
7887
7888 const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(zcu)) {
7889 .@"enum" => operand,
7890 .@"union" => blk: {
7891 if (operand_ty.unionTagType(zcu) == null) {
7892 return sema.fail(
7893 block,
7894 operand_src,
7895 "untagged union '{f}' cannot be converted to integer",
7896 .{operand_ty.fmt(pt)},
7897 );
7898 }
7899
7900 break :blk try sema.unionToTag(block, operand);
7901 },
7902 else => {
7903 return sema.fail(block, operand_src, "expected enum or tagged union, found '{f}'", .{
7904 operand_ty.fmt(pt),
7905 });
7906 },
7907 };
7908 const enum_tag_ty = sema.typeOf(enum_tag);
7909 const int_tag_ty = enum_tag_ty.backingIntType(zcu);
7910 assert(int_tag_ty.classify(zcu) != .no_possible_value);
7911
7912 if (sema.resolveValue(enum_tag)) |enum_tag_val| {
7913 if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty);
7914 return .fromValue(enum_tag_val.backingInt(zcu));
7915 }
7916
7917 try sema.requireRuntimeBlock(block, src, operand_src);
7918 return block.addTyOp(.bit_cast, int_tag_ty, enum_tag);
7919}
7920
7921fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
7922 const pt = sema.pt;
7923 const zcu = pt.zcu;
7924 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
7925 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
7926 const src = block.nodeOffset(inst_data.src_node);
7927 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
7928 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt");
7929 const operand = sema.resolveInst(extra.rhs);
7930 const operand_ty = sema.typeOf(operand);
7931
7932 if (dest_ty.zigTypeTag(zcu) != .@"enum") {
7933 return sema.fail(block, src, "expected enum, found '{f}'", .{dest_ty.fmt(pt)});
7934 }
7935 try sema.ensureLayoutResolved(dest_ty, src, .init);
7936 _ = try sema.checkIntType(block, operand_src, operand_ty);
7937
7938 if (sema.resolveValue(operand)) |int_val| {
7939 if (dest_ty.isNonexhaustiveEnum(zcu)) {
7940 const int_tag_ty = dest_ty.backingIntType(zcu);
7941 if (int_val.intFitsInType(int_tag_ty, null, zcu)) {
7942 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
7943 }
7944 return sema.fail(block, src, "int value '{f}' out of range of non-exhaustive enum '{f}'", .{
7945 int_val.fmtValueSema(pt, sema), dest_ty.fmt(pt),
7946 });
7947 }
7948 if (int_val.isUndef(zcu)) {
7949 return sema.failWithUseOfUndef(block, operand_src, null);
7950 }
7951 if (!(try sema.enumHasInt(dest_ty, int_val))) {
7952 return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{
7953 dest_ty.fmt(pt), int_val.fmtValueSema(pt, sema),
7954 });
7955 }
7956 return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern());
7957 }
7958
7959 if (try dest_ty.onePossibleValue(pt)) |opv| {
7960 if (block.wantSafety()) {
7961 // The operand is runtime-known but the result is comptime-known. In
7962 // this case we still need a safety check.
7963 const expect_int = try pt.getCoerced(opv.backingInt(zcu), operand_ty);
7964 const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int));
7965 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
7966 }
7967 return .fromValue(opv);
7968 }
7969
7970 try sema.requireRuntimeBlock(block, src, operand_src);
7971 if (block.wantSafety()) {
7972 try sema.preparePanicId(src, .invalid_enum_value);
7973 return block.addTyOp(.int_cast_safe, dest_ty, operand);
7974 }
7975 return block.addTyOp(.int_cast, dest_ty, operand);
7976}
7977
7978/// Pointer in, pointer out.
7979fn zirOptionalPayloadPtr(
7980 sema: *Sema,
7981 block: *Block,
7982 inst: Zir.Inst.Index,
7983 safety_check: bool,
7984) CompileError!Air.Inst.Ref {
7985 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
7986 const optional_ptr = sema.resolveInst(inst_data.operand);
7987 const src = block.nodeOffset(inst_data.src_node);
7988
7989 const ptr_ty = sema.typeOf(optional_ptr);
7990 assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer);
7991 try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src, .ptr_access);
7992
7993 return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false);
7994}
7995
7996/// Asserts that the layout of the pointer child type is already resolved.
7997fn analyzeOptionalPayloadPtr(
7998 sema: *Sema,
7999 block: *Block,
8000 src: LazySrcLoc,
8001 optional_ptr: Air.Inst.Ref,
8002 safety_check: bool,
8003 initializing: bool,
8004) CompileError!Air.Inst.Ref {
8005 const pt = sema.pt;
8006 const zcu = pt.zcu;
8007 const optional_ptr_ty = sema.typeOf(optional_ptr);
8008 assert(optional_ptr_ty.zigTypeTag(zcu) == .pointer);
8009
8010 const opt_type = optional_ptr_ty.childType(zcu);
8011 opt_type.assertHasLayout(zcu);
8012 if (opt_type.zigTypeTag(zcu) != .optional) {
8013 return sema.failWithExpectedOptionalType(block, src, opt_type);
8014 }
8015
8016 const child_type = opt_type.optionalChild(zcu);
8017 const child_pointer = try pt.ptrType(info: {
8018 var new = optional_ptr_ty.ptrInfo(zcu);
8019 new.child = child_type.toIntern();
8020 break :info new;
8021 });
8022
8023 if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| {
8024 if (initializing) {
8025 if (sema.isComptimeMutablePtr(ptr_val)) {
8026 // Set the optional to non-null at comptime.
8027 // If the payload is OPV, we must use that value instead of undef.
8028 const payload_val = try child_type.onePossibleValue(pt) orelse try pt.undefValue(child_type);
8029 const opt_val = try pt.intern(.{ .opt = .{
8030 .ty = opt_type.toIntern(),
8031 .val = payload_val.toIntern(),
8032 } });
8033 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(opt_val), opt_type);
8034 } else {
8035 // Emit runtime instructions to set the optional non-null bit.
8036 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
8037 try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr);
8038 }
8039 return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern());
8040 }
8041 if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| {
8042 if (val.isNull(zcu)) {
8043 return sema.fail(block, src, "unable to unwrap null", .{});
8044 }
8045 return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern());
8046 }
8047 }
8048
8049 try sema.requireRuntimeBlock(block, src, null);
8050 if (safety_check and block.wantSafety()) {
8051 const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr);
8052 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
8053 }
8054
8055 if (initializing) {
8056 const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr);
8057 try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr);
8058 return opt_payload_ptr;
8059 } else {
8060 return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr);
8061 }
8062}
8063
8064/// Value in, value out.
8065fn zirOptionalPayload(
8066 sema: *Sema,
8067 block: *Block,
8068 inst: Zir.Inst.Index,
8069 safety_check: bool,
8070) CompileError!Air.Inst.Ref {
8071 const pt = sema.pt;
8072 const zcu = pt.zcu;
8073 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
8074 const src = block.nodeOffset(inst_data.src_node);
8075 const operand = sema.resolveInst(inst_data.operand);
8076 const operand_ty = sema.typeOf(operand);
8077 const result_ty = switch (operand_ty.zigTypeTag(zcu)) {
8078 .optional => operand_ty.optionalChild(zcu),
8079 // TODO: https://github.com/ziglang/zig/issues/6597 will eliminate this branch so that we only need to handle optionals.
8080 .pointer => switch (operand_ty.ptrSize(zcu)) {
8081 .c => operand_ty, // if `ptr` is a `[*c]T`, then `ptr.?` is also a `[*c]T`
8082 .one, .many, .slice => return sema.failWithExpectedOptionalType(block, src, operand_ty),
8083 },
8084 else => return sema.failWithExpectedOptionalType(block, src, operand_ty),
8085 };
8086
8087 ct: {
8088 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
8089 if (val.optionalValue(zcu)) |payload| return .fromValue(payload); // comptime-known payload
8090 } else if (try sema.resolveIsNullFromType(block, src, operand_ty)) |is_null| {
8091 if (!is_null) break :ct; // fully runtime-known
8092 } else {
8093 break :ct; // fully runtime-known
8094 }
8095 // Comptime-known to be `null`.
8096 if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{});
8097 if (safety_check and block.wantSafety()) {
8098 try sema.safetyPanic(block, src, .unwrap_null);
8099 } else {
8100 _ = try block.addNoOp(.unreach);
8101 }
8102 return .unreachable_value;
8103 }
8104
8105 if (safety_check and block.wantSafety()) {
8106 const is_non_null = try block.addUnOp(.is_non_null, operand);
8107 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
8108 }
8109
8110 // If the payload is OPV, we need the safety check but have a comptime-known result.
8111 if (try result_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
8112
8113 return block.addTyOp(.optional_payload, result_ty, operand);
8114}
8115
8116/// Value in, value out
8117fn zirErrUnionPayload(
8118 sema: *Sema,
8119 block: *Block,
8120 inst: Zir.Inst.Index,
8121) CompileError!Air.Inst.Ref {
8122 const pt = sema.pt;
8123 const zcu = pt.zcu;
8124 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
8125 const src = block.nodeOffset(inst_data.src_node);
8126 const operand = sema.resolveInst(inst_data.operand);
8127 const operand_src = src;
8128 const err_union_ty = sema.typeOf(operand);
8129 if (err_union_ty.zigTypeTag(zcu) != .error_union) {
8130 return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{
8131 err_union_ty.fmt(pt),
8132 });
8133 }
8134 return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false);
8135}
8136
8137fn analyzeErrUnionPayload(
8138 sema: *Sema,
8139 block: *Block,
8140 src: LazySrcLoc,
8141 err_union_ty: Type,
8142 operand: Air.Inst.Ref,
8143 operand_src: LazySrcLoc,
8144 safety_check: bool,
8145) CompileError!Air.Inst.Ref {
8146 const pt = sema.pt;
8147 const zcu = pt.zcu;
8148 const payload_ty = err_union_ty.errorUnionPayload(zcu);
8149 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
8150 if (val.getErrorName(zcu).unwrap()) |name| {
8151 return sema.failWithComptimeErrorRetTrace(block, src, name);
8152 }
8153 return Air.internedToRef(zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.payload);
8154 }
8155
8156 try sema.requireRuntimeBlock(block, src, null);
8157
8158 // If the error set has no fields then no safety check is needed.
8159 if (safety_check and block.wantSafety() and
8160 !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu))
8161 {
8162 try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
8163 }
8164
8165 if (try payload_ty.onePossibleValue(pt)) |payload_opv| {
8166 return .fromValue(payload_opv);
8167 }
8168
8169 return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
8170}
8171
8172/// Pointer in, pointer out.
8173fn zirErrUnionPayloadPtr(
8174 sema: *Sema,
8175 block: *Block,
8176 inst: Zir.Inst.Index,
8177) CompileError!Air.Inst.Ref {
8178 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
8179 const operand = sema.resolveInst(inst_data.operand);
8180 const src = block.nodeOffset(inst_data.src_node);
8181
8182 const ptr_ty = sema.typeOf(operand);
8183 assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer);
8184 try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src, .ptr_access);
8185
8186 return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false);
8187}
8188
8189/// Asserts that the layout of the pointer child type is already resolved.
8190fn analyzeErrUnionPayloadPtr(
8191 sema: *Sema,
8192 block: *Block,
8193 src: LazySrcLoc,
8194 operand: Air.Inst.Ref,
8195 safety_check: bool,
8196 initializing: bool,
8197) CompileError!Air.Inst.Ref {
8198 const pt = sema.pt;
8199 const zcu = pt.zcu;
8200 const operand_ty = sema.typeOf(operand);
8201 assert(operand_ty.zigTypeTag(zcu) == .pointer);
8202
8203 if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) {
8204 return sema.fail(block, src, "expected error union type, found '{f}'", .{
8205 operand_ty.childType(zcu).fmt(pt),
8206 });
8207 }
8208
8209 const err_union_ty = operand_ty.childType(zcu);
8210 err_union_ty.assertHasLayout(zcu);
8211 const payload_ty = err_union_ty.errorUnionPayload(zcu);
8212 const operand_pointer_ty = try pt.ptrType(.{
8213 .child = payload_ty.toIntern(),
8214 .flags = .{
8215 .is_const = operand_ty.isConstPtr(zcu),
8216 .address_space = operand_ty.ptrAddressSpace(zcu),
8217 },
8218 });
8219
8220 if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| {
8221 if (initializing) {
8222 if (sema.isComptimeMutablePtr(ptr_val)) {
8223 // Set the error union to non-error at comptime.
8224 // If the payload is OPV, we must use that value instead of undef.
8225 const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty);
8226 const eu_val = try pt.intern(.{ .error_union = .{
8227 .ty = err_union_ty.toIntern(),
8228 .val = .{ .payload = payload_val.toIntern() },
8229 } });
8230 try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(eu_val), err_union_ty);
8231 } else {
8232 // Emit runtime instructions to set the error union error code.
8233 try sema.requireRuntimeBlock(block, src, null);
8234 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
8235 try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr);
8236 }
8237 return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern());
8238 }
8239 if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| {
8240 if (val.getErrorName(zcu).unwrap()) |name| {
8241 return sema.failWithComptimeErrorRetTrace(block, src, name);
8242 }
8243 return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern());
8244 }
8245 }
8246
8247 try sema.requireRuntimeBlock(block, src, null);
8248
8249 // If the error set has no fields then no safety check is needed.
8250 if (safety_check and block.wantSafety() and
8251 !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu))
8252 {
8253 try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
8254 }
8255
8256 if (initializing) {
8257 const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand);
8258 try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr);
8259 return eu_payload_ptr;
8260 } else {
8261 return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand);
8262 }
8263}
8264
8265/// Value in, value out
8266fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8267 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
8268 const src = block.nodeOffset(inst_data.src_node);
8269 const operand = sema.resolveInst(inst_data.operand);
8270 return sema.analyzeErrUnionCode(block, src, operand);
8271}
8272
8273/// If `operand` is comptime-known, asserts that it is an error value rather than a payload value.
8274fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
8275 const pt = sema.pt;
8276 const zcu = pt.zcu;
8277 const operand_ty = sema.typeOf(operand);
8278 if (operand_ty.zigTypeTag(zcu) != .error_union) {
8279 return sema.fail(block, src, "expected error union type, found '{f}'", .{
8280 operand_ty.fmt(pt),
8281 });
8282 }
8283
8284 const result_ty = operand_ty.errorUnionSet(zcu);
8285
8286 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
8287 if (val.getErrorName(zcu) == .none) return .unreachable_value;
8288 return Air.internedToRef((try pt.intern(.{ .err = .{
8289 .ty = result_ty.toIntern(),
8290 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
8291 } })));
8292 }
8293
8294 try sema.requireRuntimeBlock(block, src, null);
8295 return block.addTyOp(.unwrap_errunion_err, result_ty, operand);
8296}
8297
8298/// Pointer in, value out
8299fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
8300 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
8301 const src = block.nodeOffset(inst_data.src_node);
8302 const operand = sema.resolveInst(inst_data.operand);
8303 return sema.analyzeErrUnionCodePtr(block, src, operand);
8304}
8305
8306fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref {
8307 const pt = sema.pt;
8308 const zcu = pt.zcu;
8309 const operand_ty = sema.typeOf(operand);
8310 assert(operand_ty.zigTypeTag(zcu) == .pointer);
8311
8312 if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) {
8313 return sema.fail(block, src, "expected error union type, found '{f}'", .{
8314 operand_ty.childType(zcu).fmt(pt),
8315 });
8316 }
8317
8318 const result_ty = operand_ty.childType(zcu).errorUnionSet(zcu);
8319
8320 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
8321 if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| {
8322 if (val.getErrorName(zcu) == .none) return .unreachable_value;
8323 return Air.internedToRef((try pt.intern(.{ .err = .{
8324 .ty = result_ty.toIntern(),
8325 .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name,
8326 } })));
8327 }
8328 }
8329
8330 try sema.requireRuntimeBlock(block, src, null);
8331 return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand);
8332}
8333
8334fn zirFunc(
8335 sema: *Sema,
8336 block: *Block,
8337 inst: Zir.Inst.Index,
8338 inferred_error_set: bool,
8339) CompileError!Air.Inst.Ref {
8340 const pt = sema.pt;
8341 const zcu = pt.zcu;
8342 const comp = zcu.comp;
8343 const gpa = comp.gpa;
8344 const io = comp.io;
8345 const ip = &zcu.intern_pool;
8346
8347 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
8348 const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
8349 const target = zcu.getTarget();
8350 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node });
8351 const src = block.nodeOffset(inst_data.src_node);
8352
8353 var extra_index = extra.end;
8354
8355 const ret_ty: Type = if (extra.data.ret_ty.is_generic)
8356 .generic_poison
8357 else switch (extra.data.ret_ty.body_len) {
8358 0 => .void,
8359 1 => blk: {
8360 const ret_ty_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index]));
8361 extra_index += 1;
8362 break :blk try sema.resolveType(block, ret_ty_src, ret_ty_ref);
8363 },
8364 else => blk: {
8365 const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_ty.body_len);
8366 extra_index += ret_ty_body.len;
8367
8368 const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, .type, .{ .simple = .fn_ret_ty });
8369 break :blk ret_ty_val.toType();
8370 },
8371 };
8372
8373 var src_locs: Zir.Inst.Func.SrcLocs = undefined;
8374 const has_body = extra.data.body_len != 0;
8375 if (has_body) {
8376 extra_index += extra.data.body_len;
8377 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
8378 }
8379
8380 // If this instruction has a body, then it's a function declaration, and we decide
8381 // the callconv based on whether it is exported. Otherwise, the callconv defaults
8382 // to `.auto`.
8383 const cc: std.lang.CallingConvention = if (has_body) cc: {
8384 const func_decl_nav = sema.owner.unwrap().nav_val;
8385 const fn_is_exported = exported: {
8386 const decl_ti = ip.getNav(func_decl_nav).analysis.?.zir_index;
8387 const decl_inst = decl_ti.resolve(ip) orelse {
8388 return sema.failTransitive(.{ .lost_tracking = decl_ti });
8389 };
8390 const zir_decl = sema.code.getDeclaration(decl_inst);
8391 break :exported zir_decl.linkage == .@"export";
8392 };
8393 if (fn_is_exported) {
8394 break :cc target.cCallingConvention() orelse {
8395 // This target has no default C calling convention. We sometimes trigger a similar
8396 // error by trying to evaluate `std.lang.CallingConvention.c`, so for consistency,
8397 // let's eval that now and just get the transitive error. (It's guaranteed to error
8398 // because it does the exact `cCallingConvention` call we just did.)
8399 const cc_type = try sema.getStdLangType(src, .CallingConvention);
8400 _ = try sema.namespaceLookupVal(
8401 block,
8402 LazySrcLoc.unneeded,
8403 cc_type.getNamespaceIndex(zcu),
8404 try ip.getOrPutString(gpa, io, pt.tid, "c", .no_embedded_nulls),
8405 );
8406 // The above should have errored.
8407 @panic("std.lang is corrupt");
8408 };
8409 } else {
8410 break :cc .auto;
8411 }
8412 } else .auto;
8413
8414 return sema.funcCommon(
8415 block,
8416 inst_data.src_node,
8417 inst,
8418 cc,
8419 ret_ty,
8420 false,
8421 inferred_error_set,
8422 has_body,
8423 src_locs,
8424 0,
8425 false,
8426 );
8427}
8428
8429fn resolveGenericBody(
8430 sema: *Sema,
8431 block: *Block,
8432 src: LazySrcLoc,
8433 body: []const Zir.Inst.Index,
8434 func_inst: Zir.Inst.Index,
8435 dest_ty: Type,
8436 reason: ComptimeReason,
8437) !Value {
8438 assert(body.len != 0);
8439
8440 // Make sure any nested param instructions don't clobber our work.
8441 const prev_params = block.params;
8442 block.params = .{};
8443 defer {
8444 block.params = prev_params;
8445 }
8446
8447 const uncasted = try sema.resolveInlineBody(block, body, func_inst);
8448 const result = try sema.coerce(block, dest_ty, uncasted, src);
8449 return sema.resolveConstDefinedValue(block, src, result, reason);
8450}
8451
8452/// Given a library name, examines if the library name should end up in
8453/// `link.File.Options.windows_libs` table (for example, libc is always
8454/// specified via dedicated flag `link_libc` instead),
8455/// and puts it there if it doesn't exist.
8456/// It also dupes the library name which can then be saved as part of the
8457/// respective `Decl` (either `ExternFn` or `Var`).
8458/// The liveness of the duped library name is tied to liveness of `Zcu`.
8459/// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`).
8460pub fn handleExternLibName(
8461 sema: *Sema,
8462 block: *Block,
8463 src_loc: LazySrcLoc,
8464 lib_name: []const u8,
8465) CompileError!void {
8466 blk: {
8467 const pt = sema.pt;
8468 const zcu = pt.zcu;
8469 const comp = zcu.comp;
8470 const target = zcu.getTarget();
8471 log.debug("extern fn symbol expected in lib '{s}'", .{lib_name});
8472 if (std.zig.target.isLibCLibName(target, lib_name)) {
8473 if (!comp.config.link_libc) {
8474 return sema.fail(
8475 block,
8476 src_loc,
8477 "dependency on libc must be explicitly specified in the build command",
8478 .{},
8479 );
8480 }
8481 break :blk;
8482 }
8483 if (std.zig.target.isLibCxxLibName(target, lib_name)) {
8484 if (!comp.config.link_libcpp) return sema.fail(
8485 block,
8486 src_loc,
8487 "dependency on libc++ must be explicitly specified in the build command",
8488 .{},
8489 );
8490 break :blk;
8491 }
8492 if (mem.eql(u8, lib_name, "unwind")) {
8493 if (!comp.config.link_libunwind) return sema.fail(
8494 block,
8495 src_loc,
8496 "dependency on libunwind must be explicitly specified in the build command",
8497 .{},
8498 );
8499 break :blk;
8500 }
8501 if (!target_util.canDynamicLink(target)) {
8502 return sema.fail(
8503 block,
8504 src_loc,
8505 "dependency on dynamic library '{s}' cannot be satisfied because target does not support dynamic linking",
8506 .{lib_name},
8507 );
8508 }
8509 if (!block.ownerModule().pic and target_util.requiresPicForDynamicLink(target)) {
8510 return sema.fail(
8511 block,
8512 src_loc,
8513 "dependency on dynamic library '{s}' requires enabling Position Independent Code; fixed by '-l{s}' or '-fPIC'",
8514 .{ lib_name, lib_name },
8515 );
8516 }
8517 comp.addLinkLib(lib_name) catch |err| {
8518 return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{
8519 lib_name, @errorName(err),
8520 });
8521 };
8522 }
8523}
8524
8525/// These are calling conventions that are confirmed to work with variadic functions.
8526/// Any calling conventions not included here are either not yet verified to work with variadic
8527/// functions or there are no more other calling conventions that support variadic functions.
8528const calling_conventions_supporting_var_args = [_]std.lang.CallingConvention.Tag{
8529 .x86_16_cdecl,
8530 .x86_64_sysv,
8531 .x86_64_x32,
8532 .x86_64_win,
8533 .x86_sysv,
8534 .x86_win,
8535 .x86_mingw,
8536 .aarch64_aapcs,
8537 .aarch64_aapcs_darwin,
8538 .aarch64_aapcs_win,
8539 .aarch64_vfabi,
8540 .aarch64_vfabi_sve,
8541 .alpha_osf,
8542 .arm_aapcs,
8543 .arm_aapcs_vfp,
8544 .microblaze_std,
8545 .mips64_n64,
8546 .mips64_n32,
8547 .mips_o32,
8548 .riscv64_lp64,
8549 .riscv64_lp64_v,
8550 .riscv32_ilp32,
8551 .riscv32_ilp32_v,
8552 .sparc64_sysv,
8553 .sparc_sysv,
8554 .powerpc64_elf,
8555 .powerpc64_elf_altivec,
8556 .powerpc64_elf_v2,
8557 .powerpc_sysv,
8558 .powerpc_sysv_altivec,
8559 .powerpc_aix,
8560 .powerpc_aix_altivec,
8561 .wasm_mvp,
8562 .arc_sysv,
8563 .avr_gnu,
8564 .bpf_std,
8565 .csky_sysv,
8566 .hexagon_sysv,
8567 .hexagon_sysv_hvx,
8568 .hppa_elf,
8569 .hppa64_elf,
8570 .kvx_lp64,
8571 .kvx_ilp32,
8572 .lanai_sysv,
8573 .loongarch64_lp64,
8574 .loongarch32_ilp32,
8575 .m68k_sysv,
8576 .m68k_gnu,
8577 .m68k_rtd,
8578 .m88k_sysv,
8579 .msp430_eabi,
8580 .or1k_sysv,
8581 .s390x_sysv,
8582 .s390x_sysv_vx,
8583 .sh_gnu,
8584 .sh_renesas,
8585 .ve_sysv,
8586 .xcore_xs1,
8587 .xcore_xs2,
8588 .xtensa_call0,
8589 .xtensa_windowed,
8590};
8591fn callConvSupportsVarArgs(cc: std.lang.CallingConvention.Tag) bool {
8592 return for (calling_conventions_supporting_var_args) |supported_cc| {
8593 if (cc == supported_cc) return true;
8594 } else false;
8595}
8596fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.lang.CallingConvention.Tag) CompileError!void {
8597 const CallingConventionsSupportingVarArgsList = struct {
8598 arch: std.Target.Cpu.Arch,
8599 pub fn format(ctx: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void {
8600 var first = true;
8601 for (calling_conventions_supporting_var_args) |cc_inner| {
8602 for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| {
8603 if (supported_arch == ctx.arch) break;
8604 } else continue; // callconv not supported by this arch
8605 if (!first) {
8606 try w.writeAll(", ");
8607 }
8608 first = false;
8609 try w.print("'{s}'", .{@tagName(cc_inner)});
8610 }
8611 }
8612 };
8613
8614 if (!callConvSupportsVarArgs(cc)) {
8615 return sema.failWithOwnedErrorMsg(block, msg: {
8616 const msg = try sema.errMsg(src, "variadic function does not support '{s}' calling convention", .{@tagName(cc)});
8617 errdefer msg.destroy(sema.gpa);
8618 const target = sema.pt.zcu.getTarget();
8619 try sema.errNote(src, msg, "supported calling conventions: {f}", .{CallingConventionsSupportingVarArgsList{ .arch = target.cpu.arch }});
8620 break :msg msg;
8621 });
8622 }
8623}
8624
8625fn checkParamType(
8626 sema: *Sema,
8627 block: *Block,
8628 param_idx: u32,
8629 param_ty: Type,
8630 param_is_comptime: bool,
8631 param_is_noalias: bool,
8632 param_src: LazySrcLoc,
8633 cc: std.lang.CallingConvention,
8634) CompileError!void {
8635 const pt = sema.pt;
8636 const zcu = pt.zcu;
8637 const target = zcu.getTarget();
8638
8639 if (!param_ty.isValidParamType(zcu)) {
8640 const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
8641 return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{
8642 opaque_str, param_ty.fmt(pt),
8643 });
8644 }
8645 if (!target_util.fnCallConvAllowsZigTypes(cc)) {
8646 if (param_is_comptime) {
8647 return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{t}'", .{cc});
8648 }
8649 if (param_ty.isGenericPoison()) {
8650 return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{t}'", .{cc});
8651 }
8652 // The `validateExtern` check happens later, in `validateResolvedFuncType`.
8653 }
8654 switch (cc) {
8655 .x86_64_interrupt, .x86_interrupt => {
8656 const err_code_size = target.ptrBitWidth();
8657 switch (param_idx) {
8658 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{t}' calling convention must be a pointer type", .{cc}),
8659 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{t}' calling convention must be a {d}-bit integer", .{ cc, err_code_size }),
8660 else => return sema.fail(block, param_src, "'{t}' calling convention supports up to 2 parameters, found {d}", .{ cc, param_idx + 1 }),
8661 }
8662 },
8663 .arc_interrupt,
8664 .arm_interrupt,
8665 .microblaze_interrupt,
8666 .mips64_interrupt,
8667 .mips_interrupt,
8668 .riscv64_interrupt,
8669 .riscv32_interrupt,
8670 .sh_interrupt,
8671 .avr_interrupt,
8672 .csky_interrupt,
8673 .m68k_interrupt,
8674 .msp430_interrupt,
8675 .avr_signal,
8676 => return sema.fail(block, param_src, "parameters are not allowed with '{t}' calling convention", .{cc}),
8677 else => {},
8678 }
8679 if (param_is_noalias and !param_ty.isGenericPoison() and !param_ty.isPtrAtRuntime(zcu) and !param_ty.isSliceAtRuntime(zcu)) {
8680 return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{});
8681 }
8682 switch (target.os.tag) {
8683 .vulkan, .opengl => if (cc != .@"inline" and param_ty.isPtrAtRuntime(zcu)) {
8684 switch (param_ty.ptrAddressSpace(zcu)) {
8685 .input, .output => |as| return sema.failWithOwnedErrorMsg(block, msg: {
8686 const msg = try sema.errMsg(param_src, "function parameter cannot be a pointer in '{s}' address space", .{@tagName(as)});
8687 errdefer msg.destroy(sema.gpa);
8688 try sema.errNote(param_src, msg, "mark the function as 'inline' so the parameter is substituted at call sites", .{});
8689 break :msg msg;
8690 }),
8691 else => {},
8692 }
8693 },
8694 else => {},
8695 }
8696}
8697
8698fn checkReturnTypeAndCallConv(
8699 sema: *Sema,
8700 block: *Block,
8701 bare_ret_ty: Type,
8702 ret_ty_src: LazySrcLoc,
8703 @"callconv": std.lang.CallingConvention,
8704 callconv_src: LazySrcLoc,
8705 /// non-`null` only if the function is varargs.
8706 opt_varargs_src: ?LazySrcLoc,
8707 inferred_error_set: bool,
8708 is_noinline: bool,
8709) CompileError!void {
8710 const pt = sema.pt;
8711 const zcu = pt.zcu;
8712 const target = zcu.getTarget();
8713 if (opt_varargs_src) |varargs_src| {
8714 try sema.checkCallConvSupportsVarArgs(block, varargs_src, @"callconv");
8715 }
8716 if (inferred_error_set and !bare_ret_ty.isGenericPoison()) {
8717 try sema.validateErrorUnionPayloadType(block, bare_ret_ty, ret_ty_src);
8718 }
8719 const ies_ret_ty_prefix: []const u8 = if (inferred_error_set) "!" else "";
8720 if (!bare_ret_ty.isValidReturnType(zcu)) {
8721 const opaque_str = if (bare_ret_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else "";
8722 return sema.fail(block, ret_ty_src, "{s}return type '{s}{f}' not allowed", .{
8723 opaque_str, ies_ret_ty_prefix, bare_ret_ty.fmt(pt),
8724 });
8725 }
8726 if (!target_util.fnCallConvAllowsZigTypes(@"callconv")) {
8727 if (inferred_error_set) {
8728 return sema.fail(block, ret_ty_src, "return type '!{f}' not allowed in function with calling convention '{t}'", .{ bare_ret_ty.fmt(pt), @"callconv" });
8729 }
8730 if (bare_ret_ty.isGenericPoison()) {
8731 return sema.fail(block, ret_ty_src, "generic return type not allowed in function with calling convention '{t}'", .{@"callconv"});
8732 }
8733 // The `validateExtern` check happens later, in `validateResolvedFuncType`.
8734 }
8735 validate_incoming_stack_align: {
8736 const a: u64 = switch (@"callconv") {
8737 inline else => |payload| if (@TypeOf(payload) != void and @hasField(@TypeOf(payload), "incoming_stack_alignment"))
8738 payload.incoming_stack_alignment orelse break :validate_incoming_stack_align
8739 else
8740 break :validate_incoming_stack_align,
8741 };
8742 if (!std.math.isPowerOfTwo(a)) {
8743 return sema.fail(block, callconv_src, "calling convention incoming stack alignment '{d}' is not a power of two", .{a});
8744 }
8745 }
8746 switch (@"callconv") {
8747 .x86_64_interrupt,
8748 .x86_interrupt,
8749 .arm_interrupt,
8750 .mips64_interrupt,
8751 .mips_interrupt,
8752 .riscv64_interrupt,
8753 .riscv32_interrupt,
8754 .sh_interrupt,
8755 .arc_interrupt,
8756 .avr_interrupt,
8757 .csky_interrupt,
8758 .m68k_interrupt,
8759 .microblaze_interrupt,
8760 .msp430_interrupt,
8761 .avr_signal,
8762 => {
8763 const ret_ok = !inferred_error_set and switch (bare_ret_ty.toIntern()) {
8764 .void_type, .noreturn_type => true,
8765 else => false,
8766 };
8767 if (!ret_ok) {
8768 return sema.fail(block, ret_ty_src, "function with calling convention '{t}' must return 'void' or 'noreturn'", .{@"callconv"});
8769 }
8770 },
8771 .@"inline" => if (is_noinline) {
8772 return sema.fail(block, callconv_src, "'noinline' function cannot have calling convention 'inline'", .{});
8773 },
8774 .spirv_fragment => |fragment| {
8775 if (fragment.pixel_centered_integer and target.os.tag != .opengl) {
8776 return sema.fail(block, callconv_src, "'pixel_centered_integer' is not supported on this target", .{});
8777 }
8778 },
8779 .spirv_kernel => |kernel| {
8780 if (kernel.x == 0 or kernel.y == 0 or kernel.z == 0) {
8781 return sema.fail(block, callconv_src, "kernel workgroup dimensions must be at least 1", .{});
8782 }
8783 },
8784 .spirv_task => |task| {
8785 if (task.x == 0 or task.y == 0 or task.z == 0) {
8786 return sema.fail(block, callconv_src, "kernel workgroup dimensions must be at least 1", .{});
8787 }
8788 if (!target.cpu.has(.spirv, .mesh_shading_ext)) {
8789 return sema.fail(block, callconv_src, "calling convention '{t}' requires the 'mesh_shading_ext' feature", .{@"callconv"});
8790 }
8791 },
8792 .spirv_mesh => |mesh| {
8793 if (mesh.max_vertices == 0 or mesh.max_primitives == 0) {
8794 return sema.fail(block, callconv_src, "mesh shader 'max_vertices' and 'max_primitives' must be at least 1", .{});
8795 }
8796 if (mesh.x == 0 or mesh.y == 0 or mesh.z == 0) {
8797 return sema.fail(block, callconv_src, "mesh shader workgroup dimensions must be at least 1", .{});
8798 }
8799 if (!target.cpu.has(.spirv, .mesh_shading_ext)) {
8800 return sema.fail(block, callconv_src, "calling convention '{t}' requires the 'mesh_shading_ext' feature", .{@"callconv"});
8801 }
8802 },
8803 else => {},
8804 }
8805 switch (zcu.callconvSupported(@"callconv")) {
8806 .ok => {},
8807 .bad_arch => |allowed_archs| {
8808 const ArchListFormatter = struct {
8809 archs: []const std.Target.Cpu.Arch,
8810 pub fn format(formatter: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void {
8811 for (formatter.archs, 0..) |arch, i| {
8812 if (i != 0)
8813 try w.writeAll(", ");
8814 try w.print("'{s}'", .{@tagName(arch)});
8815 }
8816 }
8817 };
8818 return sema.fail(block, callconv_src, "calling convention '{t}' only available on architectures {f}", .{
8819 @"callconv", ArchListFormatter{ .archs = allowed_archs },
8820 });
8821 },
8822 .bad_backend => |bad_backend| return sema.fail(block, callconv_src, "calling convention '{t}' not supported by compiler backend '{t}'", .{
8823 @"callconv", bad_backend,
8824 }),
8825 }
8826}
8827
8828/// To avoid forcing type layout resolution too quickly, some validation of function types cannot be
8829/// performed when the type is first constructed, and instead must happen when either (a) a function
8830/// with that type is declared, or (b) a function with that type is called. That validation is
8831/// handled here.
8832///
8833/// Asserts that all parameter types and return types have their layout fully resolved.
8834fn validateResolvedFuncType(
8835 sema: *Sema,
8836 block: *Block,
8837 @"callconv": std.lang.CallingConvention,
8838 param_types: []const InternPool.Index,
8839 ret_ty: Type,
8840 src: LazySrcLoc,
8841 maybe_func_decl_inst: ?InternPool.TrackedInst.Index,
8842) SemaError!void {
8843 const pt = sema.pt;
8844 const zcu = pt.zcu;
8845 const gpa = zcu.comp.gpa;
8846 if (!target_util.fnCallConvAllowsZigTypes(@"callconv")) {
8847 // Check that all parameter types are extern-compatible.
8848 for (param_types, 0..) |param_ty_ip, param_index| {
8849 const param_ty: Type = .fromInterned(param_ty_ip);
8850 if (!param_ty.validateExtern(.param_ty, zcu)) {
8851 const param_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{
8852 .base_node_inst = inst,
8853 .offset = .{ .fn_proto_param = .{
8854 .fn_proto_node_offset = .zero,
8855 .param_index = @intCast(param_index),
8856 } },
8857 } else src;
8858 return sema.failWithOwnedErrorMsg(block, msg: {
8859 const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{t}'", .{
8860 param_ty.fmt(pt), @"callconv",
8861 });
8862 errdefer msg.destroy(gpa);
8863 try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty);
8864 try sema.addDeclaredHereNote(msg, param_ty);
8865 break :msg msg;
8866 });
8867 }
8868 }
8869 // Check that the return type is extern-compatible.
8870 if (!ret_ty.validateExtern(.ret_ty, zcu)) {
8871 const ret_ty_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{
8872 .base_node_inst = inst,
8873 .offset = .{ .node_offset_fn_type_ret_ty = .zero },
8874 } else src;
8875 return sema.failWithOwnedErrorMsg(block, msg: {
8876 const msg = try sema.errMsg(ret_ty_src, "return type '{f}' not allowed in function with calling convention '{t}'", .{
8877 ret_ty.fmt(pt), @"callconv",
8878 });
8879 errdefer msg.destroy(gpa);
8880 try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, ret_ty, .ret_ty);
8881 try sema.addDeclaredHereNote(msg, ret_ty);
8882 break :msg msg;
8883 });
8884 }
8885 }
8886}
8887
8888fn callConvIsCallable(cc: std.lang.CallingConvention.Tag) bool {
8889 return switch (cc) {
8890 .naked,
8891
8892 .arc_interrupt,
8893 .arm_interrupt,
8894 .avr_interrupt,
8895 .avr_signal,
8896 .csky_interrupt,
8897 .m68k_interrupt,
8898 .microblaze_interrupt,
8899 .mips_interrupt,
8900 .mips64_interrupt,
8901 .msp430_interrupt,
8902 .riscv32_interrupt,
8903 .riscv64_interrupt,
8904 .sh_interrupt,
8905 .x86_interrupt,
8906 .x86_64_interrupt,
8907
8908 .amdgcn_kernel,
8909 .nvptx_kernel,
8910 .spirv_kernel,
8911 .spirv_fragment,
8912 .spirv_vertex,
8913 .spirv_task,
8914 .spirv_mesh,
8915 => false,
8916
8917 else => true,
8918 };
8919}
8920
8921fn checkMergeAllowed(sema: *Sema, block: *Block, src: LazySrcLoc, peer_ty: Type) !void {
8922 const pt = sema.pt;
8923 const zcu = pt.zcu;
8924 const target = zcu.getTarget();
8925
8926 if (!peer_ty.isPtrAtRuntime(zcu)) {
8927 return;
8928 }
8929
8930 const as = peer_ty.ptrAddressSpace(zcu);
8931 if (!target_util.shouldBlockPointerOps(target, as)) {
8932 return;
8933 }
8934
8935 return sema.failWithOwnedErrorMsg(block, msg: {
8936 const msg = try sema.errMsg(src, "value with non-mergable pointer type '{f}' depends on runtime control flow", .{peer_ty.fmt(pt)});
8937 errdefer msg.destroy(sema.gpa);
8938
8939 const runtime_src = block.runtime_cond orelse block.runtime_loop.?;
8940 try sema.errNote(runtime_src, msg, "runtime control flow here", .{});
8941
8942 const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm);
8943 try sema.errNote(src, msg, "pointers with address space '{s}' cannot be returned from a branch on target '{s}-{s}' by compiler backend '{s}'", .{
8944 @tagName(as),
8945 @tagName(target.cpu.arch.family()),
8946 @tagName(target.os.tag),
8947 @tagName(backend),
8948 });
8949
8950 break :msg msg;
8951 });
8952}
8953
8954const Section = union(enum) {
8955 generic,
8956 default,
8957 explicit: InternPool.NullTerminatedString,
8958};
8959
8960fn funcCommon(
8961 sema: *Sema,
8962 block: *Block,
8963 src_node_offset: std.zig.Ast.Node.Offset,
8964 func_inst: Zir.Inst.Index,
8965 cc: std.lang.CallingConvention,
8966 /// this might be Type.generic_poison
8967 bare_return_type: Type,
8968 var_args: bool,
8969 inferred_error_set: bool,
8970 has_body: bool,
8971 src_locs: Zir.Inst.Func.SrcLocs,
8972 noalias_bits: u32,
8973 is_noinline: bool,
8974) CompileError!Air.Inst.Ref {
8975 const pt = sema.pt;
8976 const zcu = pt.zcu;
8977 const comp = zcu.comp;
8978 const gpa = comp.gpa;
8979 const io = comp.io;
8980 const ip = &zcu.intern_pool;
8981
8982 const src = block.nodeOffset(src_node_offset);
8983 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset });
8984 const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset });
8985
8986 var comptime_bits: u32 = 0;
8987 for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| {
8988 const param_ty: Type = .fromInterned(param_ty_ip);
8989 const is_noalias = blk: {
8990 const index = std.math.cast(u5, i) orelse break :blk false;
8991 break :blk @as(u1, @truncate(noalias_bits >> index)) != 0;
8992 };
8993 const param_src = block.src(.{ .fn_proto_param = .{
8994 .fn_proto_node_offset = src_node_offset,
8995 .param_index = @intCast(i),
8996 } });
8997 if (param_is_comptime) {
8998 comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error
8999 }
9000 try sema.checkParamType(
9001 block,
9002 @intCast(i),
9003 param_ty,
9004 param_is_comptime,
9005 is_noalias,
9006 param_src,
9007 cc,
9008 );
9009 }
9010
9011 try sema.checkReturnTypeAndCallConv(
9012 block,
9013 bare_return_type,
9014 ret_ty_src,
9015 cc,
9016 cc_src,
9017 if (var_args) block.src(.{ .fn_proto_param = .{
9018 .fn_proto_node_offset = src_node_offset,
9019 .param_index = @intCast(block.params.len),
9020 } }) else null,
9021 inferred_error_set,
9022 is_noinline,
9023 );
9024
9025 const param_types = block.params.items(.ty);
9026
9027 if (has_body) {
9028 for (param_types, 0..) |param_ty_ip, param_index| {
9029 const param_ty: Type = .fromInterned(param_ty_ip);
9030 const param_src = block.src(.{ .fn_proto_param = .{
9031 .fn_proto_node_offset = src_node_offset,
9032 .param_index = @intCast(param_index),
9033 } });
9034 try sema.ensureLayoutResolved(param_ty, param_src, .parameter);
9035 }
9036 try sema.ensureLayoutResolved(bare_return_type, ret_ty_src, .return_type);
9037 try sema.validateResolvedFuncType(
9038 block,
9039 cc,
9040 param_types,
9041 bare_return_type,
9042 src,
9043 ip.getNav(sema.owner.unwrap().nav_val).srcInst(ip),
9044 );
9045 }
9046
9047 if (inferred_error_set) {
9048 assert(has_body);
9049 return .fromIntern(try ip.getFuncDeclIes(gpa, io, pt.tid, .{
9050 .owner_nav = sema.owner.unwrap().nav_val,
9051
9052 .param_types = param_types,
9053 .noalias_bits = noalias_bits,
9054 .comptime_bits = comptime_bits,
9055 .bare_return_type = bare_return_type.toIntern(),
9056 .cc = cc,
9057 .is_var_args = var_args,
9058 .is_noinline = is_noinline,
9059
9060 .zir_body_inst = try block.trackZir(func_inst),
9061 .lbrace_line = src_locs.lbrace_line,
9062 .rbrace_line = src_locs.rbrace_line,
9063 .lbrace_column = @as(u16, @truncate(src_locs.columns)),
9064 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
9065 }));
9066 }
9067
9068 const func_ty = try ip.getFuncType(gpa, io, pt.tid, .{
9069 .param_types = param_types,
9070 .noalias_bits = noalias_bits,
9071 .comptime_bits = comptime_bits,
9072 .return_type = bare_return_type.toIntern(),
9073 .cc = cc,
9074 .is_var_args = var_args,
9075 .is_noinline = is_noinline,
9076 });
9077
9078 if (has_body) {
9079 return .fromIntern(try ip.getFuncDecl(gpa, io, pt.tid, .{
9080 .owner_nav = sema.owner.unwrap().nav_val,
9081 .ty = func_ty,
9082 .cc = cc,
9083 .is_noinline = is_noinline,
9084 .zir_body_inst = try block.trackZir(func_inst),
9085 .lbrace_line = src_locs.lbrace_line,
9086 .rbrace_line = src_locs.rbrace_line,
9087 .lbrace_column = @as(u16, @truncate(src_locs.columns)),
9088 .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)),
9089 }));
9090 }
9091
9092 return .fromIntern(func_ty);
9093}
9094
9095fn zirParam(
9096 sema: *Sema,
9097 block: *Block,
9098 inst: Zir.Inst.Index,
9099 comptime_syntax: bool,
9100) CompileError!void {
9101 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_tok;
9102 const src = block.tokenOffset(inst_data.src_tok);
9103 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
9104 const param_name: Zir.NullTerminatedString = extra.data.name;
9105 const body = sema.code.bodySlice(extra.end, extra.data.type.body_len);
9106
9107 const param_ty: Type = if (extra.data.type.is_generic) .generic_poison else ty: {
9108 // Make sure any nested param instructions don't clobber our work.
9109 const prev_params = block.params;
9110 block.params = .{};
9111 defer {
9112 block.params = prev_params;
9113 }
9114
9115 const param_ty_inst = try sema.resolveInlineBody(block, body, inst);
9116 break :ty try sema.analyzeAsType(block, src, .fn_param_types, param_ty_inst);
9117 };
9118
9119 try block.params.append(sema.arena, .{
9120 .ty = param_ty.toIntern(),
9121 .is_comptime = comptime_syntax,
9122 .name = param_name,
9123 });
9124}
9125
9126fn zirParamAnytype(
9127 sema: *Sema,
9128 block: *Block,
9129 inst: Zir.Inst.Index,
9130 comptime_syntax: bool,
9131) CompileError!void {
9132 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok;
9133 const param_name: Zir.NullTerminatedString = inst_data.start;
9134
9135 try block.params.append(sema.arena, .{
9136 .ty = .generic_poison_type,
9137 .is_comptime = comptime_syntax,
9138 .name = param_name,
9139 });
9140}
9141
9142fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9143 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9144 const src = block.nodeOffset(inst_data.src_node);
9145 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
9146 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false);
9147}
9148
9149fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9150 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9151 const src = block.nodeOffset(inst_data.src_node);
9152 const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
9153 return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true);
9154}
9155
9156fn analyzeAs(
9157 sema: *Sema,
9158 block: *Block,
9159 src: LazySrcLoc,
9160 zir_dest_type: Zir.Inst.Ref,
9161 zir_operand: Zir.Inst.Ref,
9162 no_cast_to_comptime_int: bool,
9163) CompileError!Air.Inst.Ref {
9164 const pt = sema.pt;
9165 const zcu = pt.zcu;
9166 const operand = sema.resolveInst(zir_operand);
9167 const dest_ty = try sema.resolveTypeOrPoison(block, src, zir_dest_type) orelse return operand;
9168 switch (dest_ty.zigTypeTag(zcu)) {
9169 .@"opaque" => return sema.fail(block, src, "cannot cast to opaque type '{f}'", .{dest_ty.fmt(pt)}),
9170 .noreturn => return sema.fail(block, src, "cannot cast to noreturn", .{}),
9171 else => {},
9172 }
9173
9174 const is_ret = if (zir_dest_type.toIndex()) |ptr_index|
9175 sema.code.instructions.items(.tag)[@backingInt(ptr_index)] == .ret_type
9176 else
9177 false;
9178 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) {
9179 error.NotCoercible => unreachable,
9180 else => |e| return e,
9181 };
9182}
9183
9184fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9185 const pt = sema.pt;
9186 const zcu = pt.zcu;
9187 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
9188 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9189 const operand = sema.resolveInst(inst_data.operand);
9190 const operand_ty = sema.typeOf(operand);
9191 const ptr_ty = operand_ty.scalarType(zcu);
9192 const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
9193 if (!ptr_ty.isPtrAtRuntime(zcu)) {
9194 return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)});
9195 }
9196
9197 const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;
9198 const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .usize_type, .len = len }) else .usize;
9199
9200 if (sema.resolveValue(operand)) |operand_val| ct: {
9201 if (!is_vector) {
9202 if (operand_val.isUndef(zcu)) {
9203 return .undef_usize;
9204 }
9205 const addr = operand_val.getUnsignedInt(zcu) orelse {
9206 // Wasn't an integer pointer. This is a runtime operation.
9207 break :ct;
9208 };
9209 return Air.internedToRef((try pt.intValue(
9210 .usize,
9211 addr,
9212 )).toIntern());
9213 }
9214 const new_elems = try sema.arena.alloc(InternPool.Index, len);
9215 for (new_elems, 0..) |*new_elem, i| {
9216 const ptr_val = try operand_val.elemValue(pt, i);
9217 if (ptr_val.isUndef(zcu)) {
9218 new_elem.* = .undef_usize;
9219 continue;
9220 }
9221 const addr = ptr_val.getUnsignedInt(zcu) orelse {
9222 // A vector element wasn't an integer pointer. This is a runtime operation.
9223 break :ct;
9224 };
9225 new_elem.* = (try pt.intValue(
9226 .usize,
9227 addr,
9228 )).toIntern();
9229 }
9230 return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern());
9231 }
9232 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src);
9233 try sema.validateRuntimeValue(block, ptr_src, operand);
9234 try sema.checkLogicalPtrOperation(block, ptr_src, ptr_ty);
9235 return block.addTyOp(.int_from_ptr, dest_ty, operand);
9236}
9237
9238fn zirFieldPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9239 const pt = sema.pt;
9240 const zcu = pt.zcu;
9241 const comp = zcu.comp;
9242 const gpa = comp.gpa;
9243 const io = comp.io;
9244
9245 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9246 const src = block.nodeOffset(inst_data.src_node);
9247 const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
9248 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
9249 const field_name = try zcu.intern_pool.getOrPutString(
9250 gpa,
9251 io,
9252 pt.tid,
9253 sema.code.nullTerminatedString(extra.field_name_start),
9254 .no_embedded_nulls,
9255 );
9256 const object_ptr = sema.resolveInst(extra.lhs);
9257 return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src);
9258}
9259
9260fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9261 const pt = sema.pt;
9262 const zcu = pt.zcu;
9263 const comp = zcu.comp;
9264 const gpa = comp.gpa;
9265 const io = comp.io;
9266
9267 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9268 const src = block.nodeOffset(inst_data.src_node);
9269 const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node });
9270 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
9271 const field_name = try zcu.intern_pool.getOrPutString(
9272 gpa,
9273 io,
9274 pt.tid,
9275 sema.code.nullTerminatedString(extra.field_name_start),
9276 .no_embedded_nulls,
9277 );
9278 const object_ptr = sema.resolveInst(extra.lhs);
9279 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
9280}
9281
9282fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9283 const pt = sema.pt;
9284 const zcu = pt.zcu;
9285 const comp = zcu.comp;
9286 const gpa = comp.gpa;
9287 const io = comp.io;
9288
9289 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9290 const src = block.nodeOffset(inst_data.src_node);
9291 const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node });
9292 const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
9293 const field_name = try zcu.intern_pool.getOrPutString(
9294 gpa,
9295 io,
9296 pt.tid,
9297 sema.code.nullTerminatedString(extra.field_name_start),
9298 .no_embedded_nulls,
9299 );
9300 const object_ptr = sema.resolveInst(extra.lhs);
9301 const struct_ty = sema.typeOf(object_ptr).childType(zcu);
9302 switch (struct_ty.zigTypeTag(zcu)) {
9303 .@"struct", .@"union" => {
9304 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, true);
9305 },
9306 else => {
9307 return sema.failWithStructInitNotSupported(block, src, struct_ty);
9308 },
9309 }
9310}
9311
9312fn zirFieldPtrNamedLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9313 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9314 const src = block.nodeOffset(inst_data.src_node);
9315 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
9316 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
9317 const object_ptr = sema.resolveInst(extra.lhs);
9318 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
9319 return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src);
9320}
9321
9322fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9323 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9324 const src = block.nodeOffset(inst_data.src_node);
9325 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
9326 const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
9327 const object_ptr = sema.resolveInst(extra.lhs);
9328 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
9329 return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
9330}
9331
9332fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9333 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9334 const src = block.nodeOffset(inst_data.src_node);
9335 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9336 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9337
9338 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast");
9339 const operand = sema.resolveInst(extra.rhs);
9340
9341 return sema.intCast(block, block.nodeOffset(inst_data.src_node), dest_ty, src, operand, operand_src);
9342}
9343
9344fn intCast(
9345 sema: *Sema,
9346 block: *Block,
9347 src: LazySrcLoc,
9348 dest_ty: Type,
9349 dest_ty_src: LazySrcLoc,
9350 operand: Air.Inst.Ref,
9351 operand_src: LazySrcLoc,
9352) CompileError!Air.Inst.Ref {
9353 const pt = sema.pt;
9354 const zcu = pt.zcu;
9355 const operand_ty = sema.typeOf(operand);
9356 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src);
9357 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
9358
9359 if (try sema.isComptimeKnown(operand)) {
9360 return sema.coerce(block, dest_ty, operand, operand_src);
9361 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
9362 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{});
9363 }
9364
9365 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src);
9366 const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
9367
9368 if (try dest_ty.onePossibleValue(pt)) |opv| {
9369 // requirement: intCast(u0, input) iff input == 0
9370 if (block.wantSafety()) {
9371 try sema.requireRuntimeBlock(block, src, operand_src);
9372 const wanted_info = dest_scalar_ty.intInfo(zcu);
9373 const wanted_bits = wanted_info.bits;
9374
9375 if (wanted_bits == 0) {
9376 const ok = if (is_vector) ok: {
9377 const zeros = try sema.splat(operand_ty, try pt.intValue(operand_scalar_ty, 0));
9378 const zero_inst = Air.internedToRef(zeros.toIntern());
9379 const is_in_range = try block.addCmpVector(operand, zero_inst, .eq);
9380 const all_in_range = try block.addReduce(is_in_range, .And);
9381 break :ok all_in_range;
9382 } else ok: {
9383 const zero_inst = Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern());
9384 const is_in_range = try block.addBinOp(.cmp_eq, operand, zero_inst);
9385 break :ok is_in_range;
9386 };
9387 try sema.addSafetyCheck(block, src, ok, .integer_out_of_bounds);
9388 }
9389 }
9390
9391 return Air.internedToRef(opv.toIntern());
9392 }
9393
9394 try sema.requireRuntimeBlock(block, src, operand_src);
9395 if (block.wantSafety()) {
9396 try sema.preparePanicId(src, .integer_out_of_bounds);
9397 return block.addTyOp(.int_cast_safe, dest_ty, operand);
9398 }
9399 return block.addTyOp(.int_cast, dest_ty, operand);
9400}
9401
9402fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9403 const pt = sema.pt;
9404 const zcu = pt.zcu;
9405 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9406 const src = block.nodeOffset(inst_data.src_node);
9407 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9408 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9409
9410 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast");
9411 const operand = sema.resolveInst(extra.rhs);
9412 const operand_ty = sema.typeOf(operand);
9413
9414 // Check for pointers before checking `hasBitRepresentation` so we can emit a better message for slices.
9415 switch (dest_ty.scalarType(zcu).zigTypeTag(zcu)) {
9416 .pointer, .optional => return sema.failWithOwnedErrorMsg(block, msg: {
9417 const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)});
9418 errdefer msg.destroy(sema.gpa);
9419 switch (operand_ty.zigTypeTag(zcu)) {
9420 .int, .comptime_int => try sema.errNote(src, msg, "use @ptrFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}),
9421 .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}),
9422 else => {},
9423 }
9424 break :msg msg;
9425 }),
9426 .array => switch (dest_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) {
9427 .pointer, .optional => return sema.fail(block, src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}),
9428 else => {},
9429 },
9430 else => {},
9431 }
9432 if (!dest_ty.hasBitRepresentation(zcu)) {
9433 return sema.fail(block, src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)});
9434 }
9435
9436 // Check for pointers before checking `hasBitRepresentation` so we can emit a better message for slices.
9437 switch (operand_ty.scalarType(zcu).zigTypeTag(zcu)) {
9438 .pointer, .optional => return sema.failWithOwnedErrorMsg(block, msg: {
9439 const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});
9440 errdefer msg.destroy(sema.gpa);
9441 switch (dest_ty.zigTypeTag(zcu)) {
9442 .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromPtr to cast to '{f}'", .{dest_ty.fmt(pt)}),
9443 .pointer => try sema.errNote(operand_src, msg, "use @ptrCast to cast to '{f}'", .{dest_ty.fmt(pt)}),
9444 else => {},
9445 }
9446 break :msg msg;
9447 }),
9448 .array => switch (operand_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) {
9449 .pointer, .optional => return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{dest_ty.fmt(pt)}),
9450 else => {},
9451 },
9452 else => {},
9453 }
9454 if (!operand_ty.hasBitRepresentation(zcu)) {
9455 return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)});
9456 }
9457
9458 operand_ty.assertHasLayout(zcu);
9459 try sema.ensureLayoutResolved(dest_ty, src, .init);
9460
9461 const operand_bits = operand_ty.bitSize(zcu);
9462 const dest_bits = dest_ty.bitSize(zcu);
9463 if (operand_bits != dest_bits) {
9464 return sema.fail(block, src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{
9465 dest_ty.fmt(pt),
9466 dest_bits,
9467 operand_ty.fmt(pt),
9468 operand_bits,
9469 });
9470 }
9471
9472 if (sema.resolveValue(operand)) |operand_val| {
9473 const dest_is_exhaustive_enum = dest_ty.zigTypeTag(zcu) == .@"enum" and
9474 !dest_ty.isNonexhaustiveEnum(zcu);
9475 if (dest_is_exhaustive_enum and operand_val.isUndef(zcu)) {
9476 return sema.failWithUseOfUndef(block, operand_src, null);
9477 }
9478
9479 const result_val = try sema.bitCastVal(operand_val, dest_ty);
9480
9481 if (dest_is_exhaustive_enum and
9482 dest_ty.enumTagFieldIndex(result_val, zcu) == null)
9483 {
9484 return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{
9485 dest_ty.fmt(pt), result_val.backingInt(zcu).fmtValueSema(pt, sema),
9486 });
9487 }
9488
9489 return .fromValue(result_val);
9490 }
9491
9492 try sema.validateRuntimeValue(block, src, operand);
9493
9494 if (block.wantSafety()) {
9495 try sema.preparePanicId(src, .invalid_enum_value);
9496 return block.addTyOp(.bit_cast_safe, dest_ty, operand);
9497 }
9498 return block.addTyOp(.bit_cast, dest_ty, operand);
9499}
9500
9501fn zirBackingInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9502 const pt = sema.pt;
9503 const zcu = pt.zcu;
9504 const gpa = zcu.comp.gpa;
9505 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
9506 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9507
9508 const operand = sema.resolveInst(inst_data.operand);
9509 const operand_ty = sema.typeOf(operand);
9510 operand_ty.assertHasLayout(zcu);
9511 const int_backed_ref: Air.Inst.Ref = ref: switch (operand_ty.zigTypeTag(zcu)) {
9512 .@"enum" => operand,
9513 .@"union" => {
9514 const union_obj = zcu.intern_pool.loadUnionType(operand_ty.toIntern());
9515 if (union_obj.tag_usage == .tagged) break :ref try sema.unionToTag(block, operand);
9516 if (union_obj.layout == .@"packed") break :ref operand;
9517 return sema.failWithOwnedErrorMsg(block, msg: {
9518 const msg = try sema.errMsg(operand_src, "non-packed union '{f}' does not have a backing integer", .{operand_ty.fmt(pt)});
9519 errdefer msg.deinit(gpa);
9520 try sema.errNote(operand_src, msg, "untagged union '{f}' does not have an enum tag with a backing integer", .{operand_ty.fmt(pt)});
9521 try sema.addDeclaredHereNote(msg, operand_ty);
9522 break :msg msg;
9523 });
9524 },
9525 .@"struct" => {
9526 if (operand_ty.containerLayout(zcu) != .@"packed") {
9527 return sema.fail(block, operand_src, "non-packed struct '{f}' does not have a backing integer", .{
9528 operand_ty.fmt(pt),
9529 });
9530 }
9531 break :ref operand;
9532 },
9533 else => {
9534 return sema.fail(block, operand_src, "expected enum, tagged union, packed union or packed struct, found '{f}'", .{
9535 operand_ty.fmt(pt),
9536 });
9537 },
9538 };
9539 const int_backed_ty = sema.typeOf(int_backed_ref);
9540 if (int_backed_ty.backingIntMode(zcu) != .explicit and int_backed_ty.zigTypeTag(zcu) != .@"enum")
9541 return sema.failWithAmbiguousBackingIntType(block, operand_src, int_backed_ty, "@backingInt");
9542 const backing_int_ty = int_backed_ty.backingIntType(zcu);
9543
9544 if (sema.resolveValue(int_backed_ref)) |int_backed_val| {
9545 if (int_backed_val.isUndef(zcu)) return pt.undefRef(backing_int_ty);
9546 return .fromValue(int_backed_val.backingInt(zcu));
9547 }
9548
9549 switch (backing_int_ty.classify(zcu)) {
9550 .partially_comptime, .fully_comptime => unreachable, // does not apply to integers
9551 .no_possible_value => unreachable, // enum also NPV, cannot instantiate NPV types
9552 .one_possible_value => unreachable, // enum or bitpack also OPV, should have been resolve above
9553 .runtime => {},
9554 }
9555
9556 return block.addTyOp(.bit_cast, backing_int_ty, int_backed_ref);
9557}
9558
9559fn zirFromBackingIntArgTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9560 const pt = sema.pt;
9561 const zcu = pt.zcu;
9562 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
9563 const src = block.nodeOffset(inst_data.src_node);
9564
9565 const dest_ty = try sema.resolveDestType(block, src, inst_data.operand, .remove_eu_opt, "@fromBackingInt");
9566 try sema.ensureLayoutResolved(dest_ty, src, .init);
9567 switch (dest_ty.zigTypeTag(zcu)) {
9568 .@"enum" => {},
9569 .@"struct", .@"union" => |type_tag| if (dest_ty.containerLayout(zcu) != .@"packed") {
9570 return sema.fail(block, src, "non-packed {t} '{f}' does not have a backing integer", .{
9571 type_tag, dest_ty.fmt(pt),
9572 });
9573 },
9574 else => {
9575 return sema.fail(block, src, "expected enum, packed union or packed struct, found '{f}'", .{
9576 dest_ty.fmt(pt),
9577 });
9578 },
9579 }
9580 if (dest_ty.backingIntMode(zcu) != .explicit and dest_ty.zigTypeTag(zcu) != .@"enum")
9581 return sema.failWithAmbiguousBackingIntType(block, src, dest_ty, "@fromBackingInt");
9582 return .fromType(dest_ty.backingIntType(zcu));
9583}
9584
9585fn zirFromBackingInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9586 const pt = sema.pt;
9587 const zcu = pt.zcu;
9588 const ip = &zcu.intern_pool;
9589
9590 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9591 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9592 const src = block.nodeOffset(inst_data.src_node);
9593 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9594
9595 // Type has already been validated and layout-resolved by `zirFromBackingIntArgTy`.
9596 const dest_ty = try sema.resolveDestType(block, .unneeded, extra.lhs, .remove_eu_opt, undefined);
9597 dest_ty.assertHasLayout(zcu);
9598
9599 const operand = sema.resolveInst(extra.rhs);
9600 const backing_int_ref = try sema.coerce(block, dest_ty.backingIntType(zcu), operand, operand_src);
9601
9602 if (sema.resolveValue(backing_int_ref)) |backing_int_val| {
9603 if (dest_ty.zigTypeTag(zcu) != .@"enum") {
9604 // we don't do any safety checks for bitpacks
9605 if (backing_int_val.isUndef(zcu)) return pt.undefRef(dest_ty);
9606 return .fromValue(try pt.bitpackValue(dest_ty, backing_int_val));
9607 }
9608 const enum_obj = ip.loadEnumType(dest_ty.toIntern());
9609 if (backing_int_val.isUndef(zcu)) {
9610 if (enum_obj.nonexhaustive) return pt.undefRef(dest_ty);
9611 return sema.failWithUseOfUndef(block, operand_src, null);
9612 }
9613 if (!enum_obj.nonexhaustive and
9614 enum_obj.tagValueIndex(ip, backing_int_val.toIntern()) == null)
9615 {
9616 return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{
9617 dest_ty.fmt(pt), backing_int_val.fmtValueSema(pt, sema),
9618 });
9619 }
9620 return .fromValue(try pt.enumValue(dest_ty, backing_int_val));
9621 }
9622
9623 switch (dest_ty.classify(zcu)) {
9624 .partially_comptime, .fully_comptime => unreachable, // does not apply to enums or bitpacks
9625 .no_possible_value => unreachable, // backing int also NPV, cannot coerce to NPV type
9626 .one_possible_value => unreachable, // backing int also OPV, should have been resolve above
9627 .runtime => {},
9628 }
9629
9630 if (block.wantSafety()) {
9631 try sema.preparePanicId(src, .invalid_enum_value);
9632 return block.addTyOp(.bit_cast_safe, dest_ty, backing_int_ref);
9633 }
9634 return block.addTyOp(.bit_cast, dest_ty, backing_int_ref);
9635}
9636
9637fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9638 const pt = sema.pt;
9639 const zcu = pt.zcu;
9640 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9641 const src = block.nodeOffset(inst_data.src_node);
9642 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
9643 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9644
9645 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast");
9646 const dest_scalar_ty = dest_ty.scalarType(zcu);
9647
9648 const operand = sema.resolveInst(extra.rhs);
9649 const operand_ty = sema.typeOf(operand);
9650 const operand_scalar_ty = operand_ty.scalarType(zcu);
9651
9652 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
9653 const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
9654
9655 const target = zcu.getTarget();
9656 const dest_is_comptime_float = switch (dest_scalar_ty.zigTypeTag(zcu)) {
9657 .comptime_float => true,
9658 .float => false,
9659 else => return sema.fail(
9660 block,
9661 src,
9662 "expected float or vector type, found '{f}'",
9663 .{dest_ty.fmt(pt)},
9664 ),
9665 };
9666
9667 switch (operand_scalar_ty.zigTypeTag(zcu)) {
9668 .comptime_float, .float, .comptime_int => {},
9669 else => return sema.fail(
9670 block,
9671 operand_src,
9672 "expected float or vector type, found '{f}'",
9673 .{operand_ty.fmt(pt)},
9674 ),
9675 }
9676
9677 if (sema.resolveValue(operand)) |operand_val| {
9678 if (!is_vector) {
9679 return Air.internedToRef((try operand_val.floatCast(dest_ty, pt)).toIntern());
9680 }
9681 const vec_len = operand_ty.vectorLen(zcu);
9682 const new_elems = try sema.arena.alloc(InternPool.Index, vec_len);
9683 for (new_elems, 0..) |*new_elem, i| {
9684 const old_elem = try operand_val.elemValue(pt, i);
9685 new_elem.* = (try old_elem.floatCast(dest_scalar_ty, pt)).toIntern();
9686 }
9687 return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern());
9688 }
9689 if (dest_is_comptime_float) {
9690 return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{});
9691 }
9692 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src);
9693
9694 const src_bits = operand_scalar_ty.floatBits(target);
9695 const dst_bits = dest_scalar_ty.floatBits(target);
9696 if (dst_bits >= src_bits) {
9697 return sema.coerce(block, dest_ty, operand, operand_src);
9698 }
9699 return block.addTyOp(.fptrunc, dest_ty, operand);
9700}
9701
9702fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9703 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9704 const src = block.nodeOffset(inst_data.src_node);
9705 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9706 const array = sema.resolveInst(extra.lhs);
9707 const elem_index = sema.resolveInst(extra.rhs);
9708 return sema.elemVal(block, src, array, elem_index, src, false);
9709}
9710
9711fn zirElemPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9712 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9713 const src = block.nodeOffset(inst_data.src_node);
9714 const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node });
9715 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9716 const array_ptr = sema.resolveInst(extra.lhs);
9717 const uncoerced_elem_index = sema.resolveInst(extra.rhs);
9718 if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| {
9719 const array_ptr_ty = sema.typeOf(array_ptr);
9720 if (try sema.pointerDeref(block, src, array_ptr_val, array_ptr_ty)) |array_val| {
9721 const array: Air.Inst.Ref = .fromValue(array_val);
9722 return elemVal(sema, block, src, array, uncoerced_elem_index, elem_index_src, true);
9723 }
9724 }
9725 const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src);
9726 const elem_ptr = try elemPtr(sema, block, src, array_ptr, elem_index, elem_index_src, false, true);
9727 return analyzeLoad(sema, block, src, elem_ptr, elem_index_src);
9728}
9729
9730fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9731 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].elem_val_imm;
9732 const array = sema.resolveInst(inst_data.operand);
9733 const elem_index = try sema.pt.intRef(.usize, inst_data.idx);
9734 return sema.elemVal(block, LazySrcLoc.unneeded, array, elem_index, LazySrcLoc.unneeded, false);
9735}
9736
9737fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9738 const pt = sema.pt;
9739 const zcu = pt.zcu;
9740 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9741 const src = block.nodeOffset(inst_data.src_node);
9742 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9743 const array_ptr = sema.resolveInst(extra.lhs);
9744 const elem_index = sema.resolveInst(extra.rhs);
9745 const indexable_ty = sema.typeOf(array_ptr);
9746 if (indexable_ty.zigTypeTag(zcu) != .pointer) {
9747 const capture_src = block.src(.{ .for_capture_from_input = inst_data.src_node });
9748 const msg = msg: {
9749 const msg = try sema.errMsg(capture_src, "pointer capture of non pointer type '{f}'", .{
9750 indexable_ty.fmt(pt),
9751 });
9752 errdefer msg.destroy(sema.gpa);
9753 if (indexable_ty.isIndexable(zcu)) {
9754 try sema.errNote(src, msg, "consider using '&' here", .{});
9755 }
9756 break :msg msg;
9757 };
9758 return sema.failWithOwnedErrorMsg(block, msg);
9759 }
9760 try sema.checkIndexable(block, src, indexable_ty);
9761 try sema.ensureLayoutResolved(indexable_ty.childType(zcu), src, .ptr_access);
9762 return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false);
9763}
9764
9765fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9766 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9767 const src = block.nodeOffset(inst_data.src_node);
9768 const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node });
9769 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
9770 const array_ptr = sema.resolveInst(extra.lhs);
9771 const uncoerced_elem_index = sema.resolveInst(extra.rhs);
9772 const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src);
9773 return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true);
9774}
9775
9776fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9777 const pt = sema.pt;
9778 const zcu = pt.zcu;
9779 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9780 const src = block.nodeOffset(inst_data.src_node);
9781 const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
9782 const array_ptr = sema.resolveInst(extra.ptr);
9783 const elem_index = try pt.intRef(.usize, extra.index);
9784 const array_ty = sema.typeOf(array_ptr).childType(zcu);
9785 switch (array_ty.zigTypeTag(zcu)) {
9786 .array, .vector => {},
9787 else => if (!array_ty.isTuple(zcu)) {
9788 return sema.failWithArrayInitNotSupported(block, src, array_ty);
9789 },
9790 }
9791 return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true);
9792}
9793
9794fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9795 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9796 const src = block.nodeOffset(inst_data.src_node);
9797 const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
9798 const array_ptr = sema.resolveInst(extra.lhs);
9799 const start = sema.resolveInst(extra.start);
9800 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
9801 const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node });
9802 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
9803
9804 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false);
9805}
9806
9807fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9808 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9809 const src = block.nodeOffset(inst_data.src_node);
9810 const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
9811 const array_ptr = sema.resolveInst(extra.lhs);
9812 const start = sema.resolveInst(extra.start);
9813 const end = sema.resolveInst(extra.end);
9814 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
9815 const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node });
9816 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
9817
9818 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false);
9819}
9820
9821fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9822 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9823 const src = block.nodeOffset(inst_data.src_node);
9824 const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
9825 const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
9826 const array_ptr = sema.resolveInst(extra.lhs);
9827 const start = sema.resolveInst(extra.start);
9828 const end: Air.Inst.Ref = if (extra.end == .none) .none else sema.resolveInst(extra.end);
9829 const sentinel = sema.resolveInst(extra.sentinel);
9830 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
9831 const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node });
9832 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
9833
9834 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false);
9835}
9836
9837fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9838 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
9839 const src = block.nodeOffset(inst_data.src_node);
9840 const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
9841 const array_ptr = sema.resolveInst(extra.lhs);
9842 const start = sema.resolveInst(extra.start);
9843 const len = sema.resolveInst(extra.len);
9844 const sentinel = if (extra.sentinel == .none) .none else sema.resolveInst(extra.sentinel);
9845 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
9846 const start_src = block.src(.{ .node_offset_slice_start = extra.start_src_node_offset });
9847 const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node });
9848 const sentinel_src: LazySrcLoc = if (sentinel == .none)
9849 LazySrcLoc.unneeded
9850 else
9851 block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
9852
9853 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
9854}
9855
9856fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9857 const pt = sema.pt;
9858 const zcu = pt.zcu;
9859
9860 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
9861
9862 const src = block.nodeOffset(inst_data.src_node);
9863 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
9864 const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
9865
9866 // This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will
9867 // have a double pointer if it was already a pointer.
9868
9869 const lhs_ptr_ty = sema.typeOf(sema.resolveInst(inst_data.operand));
9870 const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) {
9871 .pointer => lhs_ptr_ty.childType(zcu),
9872 else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{lhs_ptr_ty.fmt(pt)}),
9873 };
9874
9875 const sentinel_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
9876 .array => lhs_ty.childType(zcu),
9877 .pointer => switch (lhs_ty.ptrSize(zcu)) {
9878 .many, .c, .slice => lhs_ty.childType(zcu),
9879 .one => s: {
9880 const lhs_elem_ty = lhs_ty.childType(zcu);
9881 break :s switch (lhs_elem_ty.zigTypeTag(zcu)) {
9882 .array => lhs_elem_ty.childType(zcu), // array element type
9883 else => return sema.fail(block, sentinel_src, "slice of single-item pointer cannot have sentinel", .{}),
9884 };
9885 },
9886 },
9887 else => return sema.fail(block, src, "slice of non-array type '{f}'", .{lhs_ty.fmt(pt)}),
9888 };
9889
9890 return Air.internedToRef(sentinel_ty.toIntern());
9891}
9892
9893fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9894 const pt = sema.pt;
9895 const zcu = pt.zcu;
9896 const gpa = sema.gpa;
9897
9898 const zir_switch = sema.code.getSwitchBlock(inst);
9899 const src_node_offset = zir_switch.catch_or_if_src_node_offset.unwrap().?;
9900 const src = block.src(.{ .node_offset_main_token = src_node_offset });
9901 const operand_src = block.src(.{ .node_offset_if_cond = src_node_offset });
9902
9903 assert(!zir_switch.has_continue); // wrong codepath!
9904
9905 const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
9906 try sema.air_instructions.append(gpa, .{
9907 .tag = .block,
9908 .data = undefined,
9909 });
9910 var label: Block.Label = .{
9911 .zir_block = inst,
9912 .merges = .{
9913 .src_locs = .empty,
9914 .results = .empty,
9915 .br_list = .empty,
9916 .block_inst = block_inst,
9917 },
9918 };
9919 var child_block = block.makeSubBlock();
9920 child_block.label = &label;
9921 const merges = &child_block.label.?.merges;
9922 defer child_block.instructions.deinit(gpa);
9923 defer merges.deinit(gpa);
9924
9925 const non_err_case = zir_switch.non_err_case.?;
9926
9927 var non_err_block: Block = child_block.makeSubBlock();
9928 non_err_block.runtime_loop = null;
9929 non_err_block.runtime_cond = operand_src;
9930 non_err_block.runtime_index.increment();
9931 non_err_block.need_debug_scope = null;
9932 defer non_err_block.instructions.deinit(gpa);
9933
9934 var switch_block: Block = child_block.makeSubBlock();
9935 switch_block.runtime_loop = null;
9936 switch_block.runtime_cond = operand_src;
9937 switch_block.runtime_index.increment();
9938 switch_block.need_debug_scope = null;
9939 defer switch_block.instructions.deinit(gpa);
9940
9941 // We begin with unwrapping the error union we're switching on as necessary.
9942 // Then we analyze the non-error prong if it's not comptime-unreachable.
9943 // Lastly, we analyze the error prong(s) as a regular switch.
9944
9945 const raw_switch_operand, const non_err_cond, const non_err_hint = non_err: {
9946 const eu_maybe_ptr = sema.resolveInst(zir_switch.main_operand);
9947 const err_union_ty: Type = err_union_ty: {
9948 const raw_operand_ty = sema.typeOf(eu_maybe_ptr);
9949 if (!non_err_case.operand_is_ref) break :err_union_ty raw_operand_ty;
9950 try sema.checkPtrOperand(block, operand_src, raw_operand_ty);
9951 const child_ty = raw_operand_ty.childType(zcu);
9952 try sema.ensureLayoutResolved(child_ty, operand_src, .ptr_access);
9953 break :err_union_ty child_ty;
9954 };
9955 if (err_union_ty.zigTypeTag(zcu) != .error_union) {
9956 return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{
9957 err_union_ty.fmt(pt),
9958 });
9959 }
9960
9961 const non_err_cond = if (non_err_case.operand_is_ref)
9962 try sema.analyzePtrIsNonErr(block, operand_src, eu_maybe_ptr)
9963 else
9964 try sema.analyzeIsNonErr(block, operand_src, eu_maybe_ptr);
9965
9966 const non_err_hint: std.lang.BranchHint = hint: {
9967 // don't analyze the non-error body if it's unreachable
9968 if (non_err_cond == .bool_false) {
9969 break :hint undefined;
9970 }
9971
9972 const eu_payload: Air.Inst.Ref = switch (non_err_case.capture) {
9973 .by_val => try sema.analyzeErrUnionPayload(&non_err_block, src, err_union_ty, eu_maybe_ptr, operand_src, false),
9974 .by_ref => try sema.analyzeErrUnionPayloadPtr(&non_err_block, src, eu_maybe_ptr, false, false),
9975 .none => undefined,
9976 };
9977 if (non_err_case.capture != .none) sema.inst_map.putAssumeCapacity(inst, eu_payload);
9978 defer if (non_err_case.capture != .none) assert(sema.inst_map.remove(inst));
9979
9980 if (non_err_cond == .bool_true) {
9981 // Early return; we don't analyze the switch as it's unreachable.
9982 return sema.resolveBlockBody(block, src, &non_err_block, non_err_case.body, inst, merges);
9983 }
9984 break :hint try sema.analyzeBodyRuntimeBreak(&non_err_block, non_err_case.body);
9985 };
9986
9987 // Emit this into the switch block as it's our error case!
9988 const eu_code = if (non_err_case.operand_is_ref)
9989 try sema.analyzeErrUnionCodePtr(&switch_block, operand_src, eu_maybe_ptr)
9990 else
9991 try sema.analyzeErrUnionCode(&switch_block, operand_src, eu_maybe_ptr);
9992
9993 break :non_err .{
9994 eu_code,
9995 non_err_cond,
9996 non_err_hint,
9997 };
9998 };
9999
10000 const validated_switch = try sema.validateSwitchBlock(block, raw_switch_operand, false, inst, &zir_switch);
10001
10002 const maybe_switch_ref: ?Air.Inst.Ref = ref: {
10003 // make err capture (i.e. switch operand) available to switch prong bodies
10004 sema.inst_map.putAssumeCapacity(inst, raw_switch_operand);
10005 defer assert(sema.inst_map.remove(inst));
10006 break :ref try sema.analyzeSwitchBlock(block, &switch_block, raw_switch_operand, false, merges, inst, &zir_switch, &validated_switch);
10007 };
10008
10009 if (non_err_cond == .bool_false) {
10010 return maybe_switch_ref orelse {
10011 const switch_src = block.nodeOffset(zir_switch.switch_src_node_offset);
10012 return sema.resolveAnalyzedBlock(block, switch_src, &switch_block, merges, false);
10013 };
10014 }
10015
10016 if (maybe_switch_ref) |switch_ref| {
10017 if (sema.typeOf(switch_ref).isNoReturn(zcu)) {
10018 _ = try switch_block.addNoOp(.unreach);
10019 } else {
10020 const br_ref = try switch_block.addBr(merges.block_inst, switch_ref);
10021 try merges.results.append(gpa, switch_ref);
10022 try merges.br_list.append(gpa, br_ref.toIndex().?);
10023 try merges.src_locs.append(gpa, null);
10024 }
10025 }
10026
10027 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".field_names.len +
10028 non_err_block.instructions.items.len + switch_block.instructions.items.len);
10029 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
10030 .then_body_len = @intCast(non_err_block.instructions.items.len),
10031 .else_body_len = @intCast(switch_block.instructions.items.len),
10032 .branch_hints = .{
10033 .true = non_err_hint,
10034 .false = .unlikely, // errors are unlikely
10035 // Code coverage is desired for error handling.
10036 .then_cov = .poi,
10037 .else_cov = .poi,
10038 },
10039 });
10040 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(non_err_block.instructions.items));
10041 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(switch_block.instructions.items));
10042
10043 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
10044 .operand = non_err_cond,
10045 .payload = cond_br_payload,
10046 } } });
10047
10048 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
10049}
10050
10051fn zirSwitchBlock(
10052 sema: *Sema,
10053 block: *Block,
10054 inst: Zir.Inst.Index,
10055 operand_is_ref: bool,
10056) CompileError!Air.Inst.Ref {
10057 const zir_switch = sema.code.getSwitchBlock(inst);
10058
10059 const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
10060 try sema.air_instructions.append(sema.gpa, .{
10061 .tag = .block,
10062 .data = undefined,
10063 });
10064 var label: Block.Label = .{
10065 .zir_block = inst,
10066 .merges = .{
10067 .src_locs = .empty,
10068 .results = .empty,
10069 .br_list = .empty,
10070 .block_inst = block_inst,
10071 },
10072 };
10073 var child_block = block.makeSubBlock();
10074 child_block.label = &label;
10075 const merges = &child_block.label.?.merges;
10076 defer child_block.instructions.deinit(sema.gpa);
10077 defer merges.deinit(sema.gpa);
10078
10079 const raw_operand = sema.resolveInst(zir_switch.main_operand);
10080 const validated_switch = try sema.validateSwitchBlock(block, raw_operand, operand_is_ref, inst, &zir_switch);
10081 const maybe_ref = try sema.analyzeSwitchBlock(block, &child_block, raw_operand, operand_is_ref, merges, inst, &zir_switch, &validated_switch);
10082 return maybe_ref orelse {
10083 const src = block.nodeOffset(zir_switch.switch_src_node_offset);
10084 return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false);
10085 };
10086}
10087
10088/// If the switch can be resolved to a value at comptime, this will return a `Ref`
10089/// that's never `.none`.
10090/// If not, this will return `null` and emit its instructions into `child_block`.
10091fn analyzeSwitchBlock(
10092 sema: *Sema,
10093 block: *Block,
10094 child_block: *Block,
10095 raw_operand: Air.Inst.Ref,
10096 operand_is_ref: bool,
10097 merges: *Block.Merges,
10098 switch_inst: Zir.Inst.Index,
10099 zir_switch: *const Zir.UnwrappedSwitchBlock,
10100 validated_switch: *const ValidatedSwitchBlock,
10101) CompileError!?Air.Inst.Ref {
10102 const pt = sema.pt;
10103 const zcu = pt.zcu;
10104 const gpa = sema.gpa;
10105
10106 const src_node_offset = zir_switch.switch_src_node_offset;
10107 const src = block.nodeOffset(src_node_offset);
10108 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
10109
10110 const has_else = zir_switch.else_case != null;
10111 const else_case = validated_switch.else_case;
10112
10113 const operand: SwitchOperand, const operand_ty: Type, const maybe_operand_opv: ?Value, const item_ty: Type = operand: {
10114 const val, const ref = if (operand_is_ref)
10115 .{ try sema.analyzeLoad(block, src, raw_operand, operand_src), raw_operand }
10116 else
10117 .{ raw_operand, .none };
10118
10119 const operand_ty = sema.typeOf(val);
10120 operand_ty.assertHasLayout(zcu);
10121 const maybe_operand_opv = try operand_ty.onePossibleValue(pt);
10122 const init_cond: Air.Inst.Ref, const item_ty: Type = init: {
10123 if (operand_ty.zigTypeTag(zcu) == .@"union" and
10124 operand_ty.containerLayout(zcu) != .@"packed")
10125 {
10126 const tag_val = try sema.unionToTag(block, val);
10127 break :init .{ tag_val, sema.typeOf(tag_val) };
10128 }
10129 break :init .{
10130 if (maybe_operand_opv) |operand_opv| .fromValue(operand_opv) else val,
10131 operand_ty,
10132 };
10133 };
10134 item_ty.assertHasLayout(zcu);
10135
10136 if (zir_switch.has_continue and !block.isComptime()) {
10137 const operand_alloc: Air.Inst.Ref = if (zir_switch.any_maybe_runtime_capture and
10138 maybe_operand_opv == null)
10139 alloc: {
10140 const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(raw_operand));
10141 const operand_alloc = try block.addTy(.alloc, operand_ptr_ty);
10142 _ = try block.addBinOp(.store, operand_alloc, raw_operand);
10143 break :alloc operand_alloc;
10144 } else .none;
10145 break :operand .{ .{ .loop = .{
10146 .operand_alloc = operand_alloc,
10147 .operand_is_ref = operand_is_ref,
10148 .init_cond = init_cond,
10149 } }, operand_ty, maybe_operand_opv, item_ty };
10150 } else {
10151 // We always use `simple` in the comptime/OPV case, because as far as the
10152 // dispatching logic is concerned, it really is dispatching a single prong.
10153 break :operand .{ .{ .simple = .{
10154 .by_val = val,
10155 .by_ref = ref,
10156 .cond = init_cond,
10157 } }, operand_ty, maybe_operand_opv, item_ty };
10158 }
10159 };
10160
10161 const raw_operand_ty = sema.typeOf(raw_operand);
10162
10163 const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and
10164 operand_ty.containerLayout(zcu) != .@"packed";
10165 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
10166
10167 // TODO audit the `err_set` special case (https://github.com/ziglang/zig/issues/15909)
10168 if (!err_set) assert(validated_switch.case_vals.len != 0 or has_else or zir_switch.has_under); // NPV types cannot be instantiated
10169
10170 const cond_ref = switch (operand) {
10171 .simple => |s| s.cond,
10172 .loop => |l| l.init_cond,
10173 };
10174
10175 resolve_at_comptime: {
10176 // always runtime; evaluation in comptime scope uses `simple`
10177 if (operand == .loop) break :resolve_at_comptime;
10178
10179 var cur_cond_val = try sema.resolveDefinedValue(child_block, src, cond_ref) orelse {
10180 break :resolve_at_comptime;
10181 };
10182 var cur_operand = operand;
10183
10184 while (true) {
10185 if (sema.resolveSwitchBlock(
10186 block,
10187 child_block,
10188 cur_operand,
10189 raw_operand_ty,
10190 cur_cond_val,
10191 merges,
10192 switch_inst,
10193 zir_switch,
10194 validated_switch,
10195 )) |result| {
10196 return result;
10197 } else |err| switch (err) {
10198 error.ComptimeBreak => {
10199 const break_inst = sema.code.instructions.get(@backingInt(sema.comptime_break_inst));
10200 if (break_inst.tag != .switch_continue) return error.ComptimeBreak;
10201 const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data;
10202 if (extra.block_inst != switch_inst) return error.ComptimeBreak;
10203 // This is a `switch_continue` targeting this block. Change the operand and start over.
10204 const new_operand_src = child_block.nodeOffset(extra.operand_src_node.unwrap().?);
10205 const new_operand_uncoerced = sema.resolveInst(break_inst.data.@"break".operand);
10206 const new_operand = try sema.coerce(child_block, raw_operand_ty, new_operand_uncoerced, new_operand_src);
10207
10208 try sema.emitBackwardBranch(child_block, src);
10209
10210 const new_val, const new_ref = if (operand_is_ref)
10211 .{ try sema.analyzeLoad(child_block, src, new_operand, new_operand_src), new_operand }
10212 else
10213 .{ new_operand, .none };
10214
10215 const new_cond_ref = if (tagged_union_originally)
10216 try sema.unionToTag(child_block, new_val)
10217 else
10218 new_val;
10219
10220 cur_cond_val = try sema.resolveConstDefinedValue(child_block, src, new_cond_ref, null);
10221 cur_operand = .{ .simple = .{
10222 .by_val = new_val,
10223 .by_ref = new_ref,
10224 .cond = new_cond_ref,
10225 } };
10226 },
10227 else => |e| return e,
10228 }
10229 }
10230 }
10231
10232 if (child_block.isComptime()) {
10233 _ = try sema.resolveConstDefinedValue(child_block, operand_src, operand.simple.cond, null);
10234 unreachable;
10235 }
10236
10237 const switch_ref: Air.Inst.Ref, const item_has_opv = switch_ref: {
10238 const item_opv = try item_ty.onePossibleValue(pt) orelse {
10239 assert(maybe_operand_opv == null); // `operand_ty` can only be an OPV type if `item_ty` is one too!
10240 const air_ref = try sema.finishSwitchBr(
10241 block,
10242 child_block,
10243 operand,
10244 raw_operand_ty,
10245 operand_is_ref,
10246 merges,
10247 switch_inst,
10248 zir_switch,
10249 validated_switch,
10250 );
10251 break :switch_ref .{ air_ref, false };
10252 };
10253
10254 // We simplify conditions with OPV to either a `loop` or a `block` since
10255 // we cannot switch on a value which doesn't exist at runtime.
10256
10257 assert(operand == .loop); // `simple` should have already been comptime-resolved above!
10258
10259 var case_block = child_block.makeSubBlock();
10260 case_block.runtime_loop = null;
10261 case_block.runtime_cond = operand_src;
10262 case_block.runtime_index.increment();
10263 case_block.need_debug_scope = null; // this body is emitted regardless
10264 defer case_block.instructions.deinit(gpa);
10265
10266 const case_vals = validated_switch.case_vals;
10267
10268 const case_idx, const body, const capture, const has_tag_capture = find_prong: {
10269 var case_val_idx: usize = 0;
10270 var case_it = zir_switch.iterateCases();
10271 var extra_index = zir_switch.end;
10272 while (case_it.next()) |case| {
10273 const prong_info = case.prong_info;
10274 const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len);
10275 extra_index += prong_body.len;
10276 skip_case: {
10277 if (!err_set) break :skip_case;
10278 // This case might consist of errors which are not in the set
10279 // we're switching on. If so we have to skip it!
10280 const item_refs = case_vals[case_val_idx..][0..case.item_infos.len];
10281 case_val_idx += item_refs.len;
10282 assert(case.range_infos.len == 0);
10283 for (case.item_infos, item_refs) |item_info, item_ref| {
10284 if (item_info.bodyLen()) |body_len| extra_index += body_len;
10285 if (sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, false, true, prong_info.is_comptime_unreach)) {
10286 break :skip_case;
10287 }
10288 }
10289 continue;
10290 }
10291 break :find_prong .{ case.index, prong_body, prong_info.capture, prong_info.has_tag_capture };
10292 }
10293 if (has_else) {
10294 // This *has* to be checked after iterating all regular cases because
10295 // we allow simple noreturn else prongs when switching on error sets!
10296 break :find_prong .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture };
10297 }
10298 unreachable; // malformed validated switch
10299 };
10300
10301 const analyze_body = sema.wantSwitchProngBodyAnalysis(.fromValue(item_opv), operand_ty, tagged_union_originally, err_set, false);
10302 if (!analyze_body) return .unreachable_value;
10303
10304 if (!(err_set and
10305 try sema.maybeErrorUnwrap(&case_block, body, cond_ref, operand_src, true)))
10306 {
10307 const payload_inst = if (capture != .none) inst: {
10308 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
10309 const payload_ref: Air.Inst.Ref = payload_ref: {
10310 const captured_opv: Value = captured_opv: {
10311 if (!tagged_union_originally) {
10312 break :captured_opv item_opv;
10313 }
10314 if (maybe_operand_opv) |operand_opv| {
10315 break :captured_opv .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val);
10316 }
10317 assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture
10318 const loaded_operand = try sema.analyzeSwitchOperandLoad(&case_block, operand, operand_src, capture == .by_ref);
10319 break :payload_ref try sema.resolveSwitchPayloadCaptureTaggedUnion(
10320 &case_block,
10321 loaded_operand,
10322 operand_src,
10323 operand_ty,
10324 item_opv,
10325 capture == .by_ref,
10326 );
10327 };
10328 break :payload_ref switch (capture) {
10329 .by_val => .fromValue(captured_opv),
10330 .by_ref => try sema.uavRef(captured_opv),
10331 .none => unreachable,
10332 };
10333 };
10334 sema.inst_map.putAssumeCapacity(payload_inst, payload_ref);
10335 break :inst payload_inst;
10336 } else undefined;
10337 defer if (capture != .none) assert(sema.inst_map.remove(payload_inst));
10338
10339 const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: {
10340 const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
10341 if (!tagged_union_originally) {
10342 const tag_capture_src = block.src(.{ .switch_tag_capture = .{
10343 .switch_node_offset = src_node_offset,
10344 .case_idx = case_idx,
10345 } });
10346 return sema.failWithInvalidSwitchTagCapture(block, tag_capture_src, operand_ty);
10347 }
10348 sema.inst_map.putAssumeCapacity(tag_inst, .fromValue(item_opv));
10349 break :inst tag_inst;
10350 } else undefined;
10351 defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst));
10352
10353 if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty));
10354 defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst));
10355
10356 _ = try sema.analyzeBodyRuntimeBreak(&case_block, body);
10357 }
10358
10359 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len +
10360 case_block.instructions.items.len);
10361 const payload_index = sema.addExtraAssumeCapacity(Air.Block{
10362 .body_len = @intCast(case_block.instructions.items.len),
10363 });
10364 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
10365
10366 const air_tag: Air.Inst.Tag = if (merges.extra_insts.items.len > 0)
10367 .loop
10368 else
10369 .block;
10370 const air_ref = try child_block.addInst(.{
10371 .tag = air_tag,
10372 .data = .{ .ty_pl = .{
10373 .ty = .noreturn,
10374 .payload = payload_index,
10375 } },
10376 });
10377 break :switch_ref .{ air_ref, true };
10378 };
10379
10380 const air_tag = sema.air_instructions.items(.tag)[@backingInt(switch_ref.toIndex().?)];
10381 switch (air_tag) {
10382 .loop_switch_br, .switch_br => assert(!item_has_opv),
10383 .loop, .block => assert(item_has_opv),
10384 else => unreachable,
10385 }
10386 switch (air_tag) {
10387 .loop_switch_br, .loop => assert(merges.extra_insts.items.len > 0),
10388 .switch_br, .block => assert(merges.extra_insts.items.len == 0),
10389 else => unreachable,
10390 }
10391
10392 // We're done with analyzing the switch statement! Now all we have to do is
10393 // replace the placeholder `br` insts inserted by `zirSwitchContinue`s with
10394 // their respective finalized inst pointing back at `switch_ref`.
10395
10396 for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| {
10397 var replacement_block = block.makeSubBlock();
10398 defer replacement_block.instructions.deinit(gpa);
10399
10400 assert(sema.air_instructions.items(.tag)[@backingInt(placeholder_inst)] == .br);
10401 const new_operand_maybe_ref = sema.air_instructions.items(.data)[@backingInt(placeholder_inst)].br.operand;
10402
10403 if (zir_switch.any_maybe_runtime_capture and !item_has_opv) {
10404 _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref);
10405 }
10406
10407 const new_operand_val = if (operand_is_ref)
10408 try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src)
10409 else
10410 new_operand_maybe_ref;
10411
10412 const new_cond = try sema.coerce(&replacement_block, item_ty, new_operand_val, dispatch_src);
10413
10414 if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and
10415 item_ty.zigTypeTag(zcu) == .@"enum" and !item_ty.isNonexhaustiveEnum(zcu) and
10416 !item_has_opv and !try sema.isComptimeKnown(new_cond))
10417 {
10418 const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond);
10419 try sema.addSafetyCheck(&replacement_block, src, ok, .corrupt_switch);
10420 }
10421
10422 if (item_has_opv) {
10423 _ = try replacement_block.addInst(.{
10424 .tag = .repeat,
10425 .data = .{ .repeat = .{
10426 .loop_inst = switch_ref.toIndex().?,
10427 } },
10428 });
10429 } else {
10430 _ = try replacement_block.addInst(.{
10431 .tag = .switch_dispatch,
10432 .data = .{ .br = .{
10433 .block_inst = switch_ref.toIndex().?,
10434 .operand = new_cond,
10435 } },
10436 });
10437 }
10438
10439 if (replacement_block.instructions.items.len == 1) {
10440 // Optimization: we don't need a block!
10441 sema.air_instructions.set(
10442 @backingInt(placeholder_inst),
10443 sema.air_instructions.get(@backingInt(replacement_block.instructions.items[0])),
10444 );
10445 continue;
10446 }
10447
10448 // Replace placeholder with a block.
10449 // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`.
10450 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len +
10451 replacement_block.instructions.items.len);
10452 sema.air_instructions.set(@backingInt(placeholder_inst), .{
10453 .tag = .block,
10454 .data = .{ .ty_pl = .{
10455 .ty = .noreturn,
10456 .payload = sema.addExtraAssumeCapacity(Air.Block{
10457 .body_len = @intCast(replacement_block.instructions.items.len),
10458 }),
10459 } },
10460 });
10461 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items));
10462 }
10463
10464 return null;
10465}
10466
10467fn finishSwitchBr(
10468 sema: *Sema,
10469 block: *Block,
10470 child_block: *Block,
10471 operand: SwitchOperand,
10472 raw_operand_ty: Type,
10473 operand_is_ref: bool,
10474 merges: *Block.Merges,
10475 switch_inst: Zir.Inst.Index,
10476 zir_switch: *const Zir.UnwrappedSwitchBlock,
10477 validated_switch: *const ValidatedSwitchBlock,
10478) CompileError!Air.Inst.Ref {
10479 const pt = sema.pt;
10480 const zcu = pt.zcu;
10481 const ip = &zcu.intern_pool;
10482 const gpa = sema.gpa;
10483
10484 const src_node_offset = zir_switch.switch_src_node_offset;
10485 const src = block.nodeOffset(src_node_offset);
10486 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
10487
10488 const has_else = zir_switch.else_case != null;
10489 const has_under = zir_switch.has_under;
10490
10491 const else_case = validated_switch.else_case;
10492
10493 const scalar_cases_len = zir_switch.scalarCasesLen();
10494 const multi_cases_len = zir_switch.multiCasesLen();
10495
10496 const operand_ty = if (operand_is_ref)
10497 raw_operand_ty.childType(zcu)
10498 else
10499 raw_operand_ty;
10500
10501 const cond_ref = switch (operand) {
10502 .simple => |s| s.cond,
10503 .loop => |l| l.init_cond,
10504 };
10505
10506 // AstGen guarantees that the instruction immediately preceding
10507 // switch_block[_ref]/switch_block_err_union is a dbg_stmt.
10508 const cond_dbg_node_index: Zir.Inst.Index = @fromBackingInt(@intCast(@backingInt(switch_inst) - 1));
10509
10510 const else_is_named_only = has_else and has_under;
10511
10512 const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and
10513 operand_ty.containerLayout(zcu) != .@"packed";
10514 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
10515
10516 const item_ty = if (tagged_union_originally)
10517 operand_ty.unionTagType(zcu).?
10518 else
10519 operand_ty;
10520
10521 const estimated_cases_len: u32 = scalar_cases_len + multi_cases_len +
10522 @intFromBool(has_else or has_under);
10523
10524 const BranchHints = struct {
10525 bags: std.ArrayList(u32),
10526 count: u32,
10527 const hints_per_bag = 10;
10528 fn ensureUnusedCapacity(hints: *@This(), gpa_inner: Allocator, additional_count: u32) Allocator.Error!void {
10529 const unused_hints = hints.bags.capacity * hints_per_bag - hints.count;
10530 if (unused_hints >= additional_count) return;
10531 const bags_required = @divCeil(hints.count + additional_count, hints_per_bag);
10532 return hints.bags.ensureUnusedCapacity(gpa_inner, bags_required);
10533 }
10534 fn appendAssumeCapacity(hints: *@This(), hint: std.lang.BranchHint) void {
10535 const idx_in_bag = hints.count % hints_per_bag;
10536 var bag: u32 = if (idx_in_bag > 0) hints.bags.pop().? else 0;
10537 bag |= @as(u32, @backingInt(hint)) << @intCast(@bitSizeOf(std.lang.BranchHint) * idx_in_bag);
10538 hints.count += 1;
10539 return hints.bags.appendAssumeCapacity(bag);
10540 }
10541 fn append(hints: *@This(), gpa_inner: Allocator, hint: std.lang.BranchHint) Allocator.Error!void {
10542 try hints.ensureUnusedCapacity(gpa_inner, 1);
10543 return hints.appendAssumeCapacity(hint);
10544 }
10545 };
10546 var branch_hints: BranchHints = hints: {
10547 const num_bags = @divCeil(estimated_cases_len, BranchHints.hints_per_bag);
10548 break :hints .{ .bags = try .initCapacity(gpa, num_bags), .count = 0 };
10549 };
10550 defer branch_hints.bags.deinit(gpa);
10551
10552 var cases_extra: std.ArrayList(u32) = try .initCapacity(gpa, estimated_cases_len *
10553 @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len);
10554 defer cases_extra.deinit(gpa);
10555
10556 // We will reuse this block for each case.
10557 var case_block = child_block.makeSubBlock();
10558 case_block.runtime_loop = null;
10559 case_block.runtime_cond = operand_src;
10560 case_block.runtime_index.increment();
10561 case_block.need_debug_scope = null; // this body is emitted regardless
10562 defer case_block.instructions.deinit(gpa);
10563
10564 const case_vals = validated_switch.case_vals;
10565 var case_val_idx: usize = 0;
10566 var case_it = zir_switch.iterateCases();
10567 var extra_index = zir_switch.end;
10568
10569 var under_prong: ?struct {
10570 index: Zir.UnwrappedSwitchBlock.Case.Index,
10571 body: []const Zir.Inst.Index,
10572 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
10573 has_tag_capture: bool,
10574 } = null;
10575
10576 var cases_len: u32 = 0;
10577 while (case_it.next()) |case| {
10578 const item_refs = case_vals[case_val_idx..][0..case.item_infos.len];
10579 case_val_idx += item_refs.len;
10580 const range_refs: []const [2]Air.Inst.Ref =
10581 @ptrCast(case_vals[case_val_idx..][0 .. 2 * case.range_infos.len]);
10582 case_val_idx += 2 * range_refs.len;
10583
10584 const prong_info = case.prong_info;
10585 const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len);
10586 extra_index += prong_body.len;
10587
10588 // Enough capacity for inlining regular items, we can't really predict
10589 // how many range items we will end up with (at least not in a safe and
10590 // cheap manner) so we allocate on demand for those.
10591 if (prong_info.is_inline) {
10592 try branch_hints.ensureUnusedCapacity(gpa, @intCast(case.item_infos.len));
10593 }
10594
10595 var emit_bb = false;
10596 var any_analyze_body = false;
10597 var is_under_prong = false;
10598 for (case.item_infos, item_refs, 0..) |item_info, item_ref, item_i| {
10599 if (item_ref == .none) is_under_prong = true;
10600 if (item_info.bodyLen()) |body_len| extra_index += body_len;
10601
10602 const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, tagged_union_originally, err_set, prong_info.is_comptime_unreach);
10603 if (analyze_body) any_analyze_body = true;
10604
10605 if (prong_info.is_inline) {
10606 cases_len += 1;
10607 case_block.instructions.clearRetainingCapacity();
10608 case_block.error_return_trace_index = child_block.error_return_trace_index;
10609
10610 if (emit_bb) {
10611 const bb_src = block.src(.{ .switch_case_item = .{
10612 .switch_node_offset = src_node_offset,
10613 .case_idx = case.index,
10614 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
10615 } });
10616 try sema.emitBackwardBranch(block, bb_src);
10617 }
10618 emit_bb = true;
10619
10620 const prong_hint: std.lang.BranchHint = hint: {
10621 if (analyze_body) break :hint try sema.analyzeSwitchProng(
10622 &case_block,
10623 operand,
10624 operand_ty,
10625 raw_operand_ty,
10626 prong_body,
10627 block.src(.{ .switch_capture = .{
10628 .switch_node_offset = src_node_offset,
10629 .case_idx = case.index,
10630 } }),
10631 prong_info.capture,
10632 prong_info.has_tag_capture,
10633 .{ .@"inline" = item_ref },
10634 validated_switch.else_err_ty,
10635 switch_inst,
10636 zir_switch,
10637 );
10638 _ = try case_block.addNoOp(.unreach);
10639 break :hint .cold; // unreachable branches are cold
10640 };
10641 branch_hints.appendAssumeCapacity(prong_hint);
10642
10643 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len +
10644 1 + // `item`, no ranges
10645 case_block.instructions.items.len);
10646 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
10647 .items_len = 1,
10648 .ranges_len = 0,
10649 .body_len = @intCast(case_block.instructions.items.len),
10650 }));
10651 cases_extra.appendAssumeCapacity(@backingInt(item_ref));
10652 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
10653 }
10654 }
10655 for (case.range_infos, range_refs, 0..) |range_info, range_ref, range_i| {
10656 if (range_info[0].bodyLen()) |body_len| extra_index += body_len;
10657 if (range_info[1].bodyLen()) |body_len| extra_index += body_len;
10658
10659 any_analyze_body = true; // always an integer range, always needs analysis
10660
10661 if (prong_info.is_inline) {
10662 var item = sema.resolveValue(range_ref[0]).?;
10663 const item_last = sema.resolveValue(range_ref[1]).?;
10664
10665 if (item.getUnsignedInt(zcu)) |first_int| {
10666 if (item_last.getUnsignedInt(zcu)) |last_int| {
10667 if (std.math.cast(u32, last_int - first_int)) |range_len| {
10668 try branch_hints.ensureUnusedCapacity(gpa, range_len);
10669 }
10670 }
10671 }
10672
10673 var prev_result_overflowed = false;
10674 while (item.compareScalar(.lte, item_last, item_ty, zcu)) : ({
10675 assert(!prev_result_overflowed);
10676 const result = try arith.incrementDefinedInt(sema, item_ty, item);
10677 prev_result_overflowed = result.overflow;
10678 item = result.val;
10679 }) {
10680 cases_len += 1;
10681 case_block.instructions.clearRetainingCapacity();
10682 case_block.error_return_trace_index = child_block.error_return_trace_index;
10683
10684 const item_ref: Air.Inst.Ref = .fromValue(item);
10685
10686 if (emit_bb) {
10687 const bb_src = block.src(.{ .switch_case_item = .{
10688 .switch_node_offset = src_node_offset,
10689 .case_idx = case.index,
10690 .item_idx = .{ .kind = .range, .value = @intCast(range_i) },
10691 } });
10692 try sema.emitBackwardBranch(block, bb_src);
10693 }
10694 emit_bb = true;
10695
10696 const prong_hint = try sema.analyzeSwitchProng(
10697 &case_block,
10698 operand,
10699 operand_ty,
10700 raw_operand_ty,
10701 prong_body,
10702 block.src(.{ .switch_capture = .{
10703 .switch_node_offset = src_node_offset,
10704 .case_idx = case.index,
10705 } }),
10706 prong_info.capture,
10707 prong_info.has_tag_capture,
10708 .{ .@"inline" = item_ref },
10709 validated_switch.else_err_ty,
10710 switch_inst,
10711 zir_switch,
10712 );
10713 try branch_hints.append(gpa, prong_hint);
10714
10715 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len +
10716 1 + // `item`, no ranges
10717 case_block.instructions.items.len);
10718 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
10719 .items_len = 1,
10720 .ranges_len = 0,
10721 .body_len = @intCast(case_block.instructions.items.len),
10722 }));
10723 cases_extra.appendAssumeCapacity(@backingInt(item_ref));
10724 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
10725 }
10726 }
10727 }
10728
10729 if (prong_info.is_inline) continue; // handled above
10730
10731 if (is_under_prong) {
10732 // We will handle this later. If there are any named items specified
10733 // along with the `_`, we don't have to actually emit any AIR for them
10734 // as they will be 'absorbed' by the `_` (the catch-all prong) anyway.
10735 under_prong = .{
10736 .index = case.index,
10737 .body = prong_body,
10738 .capture = case.prong_info.capture,
10739 .has_tag_capture = case.prong_info.has_tag_capture,
10740 };
10741 continue;
10742 }
10743
10744 cases_len += 1;
10745 case_block.instructions.clearRetainingCapacity();
10746 case_block.error_return_trace_index = child_block.error_return_trace_index;
10747
10748 const prong_hint: std.lang.BranchHint = hint: {
10749 if (any_analyze_body) break :hint try sema.analyzeSwitchProng(
10750 &case_block,
10751 operand,
10752 operand_ty,
10753 raw_operand_ty,
10754 prong_body,
10755 block.src(.{ .switch_capture = .{
10756 .switch_node_offset = src_node_offset,
10757 .case_idx = case.index,
10758 } }),
10759 prong_info.capture,
10760 prong_info.has_tag_capture,
10761 if (range_refs.len > 0) .has_ranges else .{ .item_refs = item_refs },
10762 validated_switch.else_err_ty,
10763 switch_inst,
10764 zir_switch,
10765 );
10766 _ = try case_block.addNoOp(.unreach);
10767 break :hint .cold; // unreachable branches are cold
10768 };
10769 try branch_hints.append(gpa, prong_hint);
10770
10771 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len +
10772 item_refs.len +
10773 2 * range_refs.len +
10774 case_block.instructions.items.len);
10775 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
10776 .items_len = @intCast(item_refs.len),
10777 .ranges_len = @intCast(range_refs.len),
10778 .body_len = @intCast(case_block.instructions.items.len),
10779 }));
10780 cases_extra.appendSliceAssumeCapacity(@ptrCast(item_refs));
10781 cases_extra.appendSliceAssumeCapacity(@ptrCast(range_refs));
10782 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
10783 }
10784
10785 const catch_all_extra: []const u32 = catch_all_extra: {
10786 if (!has_else and !has_under and !case_block.wantSafety()) {
10787 try branch_hints.append(gpa, .none);
10788 break :catch_all_extra &.{};
10789 }
10790 var emit_bb = false;
10791 if (has_else and else_case.is_inline) {
10792 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
10793 const error_names, const min_int = check_enumerable: {
10794 switch (item_ty.zigTypeTag(zcu)) {
10795 .@"enum" => if (else_is_named_only or
10796 !item_ty.isNonexhaustiveEnum(zcu) or tagged_union_originally)
10797 {
10798 try branch_hints.ensureUnusedCapacity(gpa, @intCast(validated_switch.seen.enum_fields.len));
10799 break :check_enumerable .{ undefined, undefined };
10800 },
10801 .error_set => if (!operand_ty.isAnyError(zcu)) {
10802 const error_names = item_ty.errorSetNames(zcu);
10803 try branch_hints.ensureUnusedCapacity(gpa, error_names.len);
10804 break :check_enumerable .{ error_names, undefined };
10805 },
10806 .int => {
10807 const min_int = try item_ty.minInt(pt, item_ty);
10808 break :check_enumerable .{ undefined, min_int };
10809 },
10810 .@"union", .@"struct" => {
10811 const backing_int_ty = item_ty.backingIntType(zcu);
10812 const min_backing_int = try backing_int_ty.minInt(pt, backing_int_ty);
10813 break :check_enumerable .{ undefined, min_backing_int };
10814 },
10815 .bool, .void => break :check_enumerable .{ undefined, undefined },
10816 else => {},
10817 }
10818 return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{
10819 item_ty.fmt(pt),
10820 });
10821 };
10822 var unhandled_it = validated_switch.iterateUnhandledItems(error_names, min_int);
10823 while (try unhandled_it.next(sema, item_ty)) |item_val| {
10824 cases_len += 1;
10825 case_block.instructions.clearRetainingCapacity();
10826 case_block.error_return_trace_index = child_block.error_return_trace_index;
10827
10828 const item_ref: Air.Inst.Ref = .fromValue(item_val);
10829
10830 const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, tagged_union_originally, err_set, false);
10831
10832 if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src);
10833 emit_bb = true;
10834
10835 const prong_hint: std.lang.BranchHint = hint: {
10836 if (analyze_body) break :hint try sema.analyzeSwitchProng(
10837 &case_block,
10838 operand,
10839 operand_ty,
10840 raw_operand_ty,
10841 else_case.body,
10842 block.src(.{ .switch_capture = .{
10843 .switch_node_offset = src_node_offset,
10844 .case_idx = else_case.index,
10845 } }),
10846 else_case.capture,
10847 else_case.has_tag_capture,
10848 .{ .@"inline" = item_ref },
10849 validated_switch.else_err_ty,
10850 switch_inst,
10851 zir_switch,
10852 );
10853 _ = try case_block.addNoOp(.unreach);
10854 break :hint .cold; // unreachable branches are cold
10855 };
10856 try branch_hints.append(gpa, prong_hint);
10857
10858 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len +
10859 1 + // `item`, no ranges
10860 case_block.instructions.items.len);
10861 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
10862 .items_len = 1,
10863 .ranges_len = 0,
10864 .body_len = @intCast(case_block.instructions.items.len),
10865 }));
10866 cases_extra.appendAssumeCapacity(@backingInt(item_ref));
10867 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
10868 }
10869 }
10870
10871 case_block.instructions.clearRetainingCapacity();
10872 case_block.error_return_trace_index = child_block.error_return_trace_index;
10873
10874 if (zcu.backendSupportsFeature(.is_named_enum_value) and
10875 (has_else or has_under) and block.wantSafety() and
10876 item_ty.zigTypeTag(zcu) == .@"enum" and
10877 (!operand_ty.isNonexhaustiveEnum(zcu) or tagged_union_originally))
10878 {
10879 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
10880 const ok = try case_block.addUnOp(.is_named_enum_value, cond_ref);
10881 try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch);
10882 }
10883
10884 if (else_is_named_only and !else_case.is_inline) {
10885 // If we have both an `else` and an `_` prong, all named values go
10886 // into the `else` prong and all unnamed ones go into the `_` prong.
10887 // We will manually enumerate all named values which haven't been
10888 // encountered yet and create an extra prong for them, which will
10889 // evaulate to the `else` body.
10890
10891 assert(operand_ty.isNonexhaustiveEnum(zcu));
10892
10893 cases_len += 1;
10894
10895 const prong_hint: std.lang.BranchHint = hint: {
10896 if (!else_case.is_inline) break :hint try sema.analyzeSwitchProng(
10897 &case_block,
10898 operand,
10899 operand_ty,
10900 raw_operand_ty,
10901 else_case.body,
10902 block.src(.{ .switch_capture = .{
10903 .switch_node_offset = src_node_offset,
10904 .case_idx = else_case.index,
10905 } }),
10906 else_case.capture,
10907 else_case.has_tag_capture,
10908 .special,
10909 validated_switch.else_err_ty,
10910 switch_inst,
10911 zir_switch,
10912 );
10913 _ = try case_block.addNoOp(.unreach);
10914 break :hint .cold; // unreachable branches are cold
10915 };
10916 try branch_hints.append(gpa, prong_hint);
10917
10918 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len +
10919 (validated_switch.seen.enum_fields.len + 1 - zir_switch.totalItemsLen()) + // +1 because totalItemsLen includes the _
10920 case_block.instructions.items.len);
10921 const extra_case = cases_extra.addManyAsArrayAssumeCapacity(
10922 @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len,
10923 );
10924 var items_len: u32 = 0;
10925 for (validated_switch.seen.enum_fields, 0..) |seen_field, field_i| {
10926 if (seen_field != null) continue;
10927 const item_val = try pt.enumValueFieldIndex(item_ty, @intCast(field_i));
10928 const item_ref: Air.Inst.Ref = .fromValue(item_val);
10929 cases_extra.appendAssumeCapacity(@backingInt(item_ref));
10930 items_len += 1;
10931 }
10932 assert(items_len > 0); // `else` must be reachable at this point
10933 extra_case.* = payloadToExtraItems(Air.SwitchBr.Case{
10934 .items_len = items_len,
10935 .ranges_len = 0,
10936 .body_len = @intCast(case_block.instructions.items.len),
10937 });
10938 cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
10939
10940 // We fall through to the regular catch-all prong generation.
10941
10942 case_block.instructions.clearRetainingCapacity();
10943 case_block.error_return_trace_index = child_block.error_return_trace_index;
10944 }
10945
10946 const analyze_catch_all_body = analyze_body: {
10947 if (has_under) {
10948 assert(!tagged_union_originally);
10949 assert(!err_set);
10950 break :analyze_body true; // can never be inline
10951 } else if (has_else) {
10952 if (else_case.is_inline) break :analyze_body false; // already handled above
10953 } else {
10954 break :analyze_body false; // we still may want a safety check!
10955 }
10956 if (tagged_union_originally) {
10957 const union_obj = zcu.typeToUnion(operand_ty).?;
10958 for (validated_switch.seen.enum_fields, 0..) |seen_field, field_i| {
10959 if (seen_field != null) continue;
10960 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_i]);
10961 if (!field_ty.isNoReturn(zcu)) break :analyze_body true;
10962 }
10963 break :analyze_body false;
10964 }
10965 if (err_set) {
10966 const else_err_ty = validated_switch.else_err_ty orelse {
10967 assert(else_case.is_simple_noreturn);
10968 break :analyze_body false;
10969 };
10970 if (else_err_ty.errorSetIsEmpty(zcu)) break :analyze_body false;
10971 }
10972 break :analyze_body true;
10973 };
10974
10975 const catch_all_hint = hint: {
10976 if (analyze_catch_all_body) {
10977 const index, const body, const capture, const has_tag_capture = if (under_prong) |under|
10978 .{ under.index, under.body, under.capture, under.has_tag_capture }
10979 else
10980 .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture };
10981 break :hint try sema.analyzeSwitchProng(
10982 &case_block,
10983 operand,
10984 operand_ty,
10985 raw_operand_ty,
10986 body,
10987 block.src(.{ .switch_capture = .{
10988 .switch_node_offset = src_node_offset,
10989 .case_idx = index,
10990 } }),
10991 capture,
10992 has_tag_capture,
10993 .special,
10994 validated_switch.else_err_ty,
10995 switch_inst,
10996 zir_switch,
10997 );
10998 }
10999 // We still need a terminator in this block, but we have proven
11000 // that it is unreachable.
11001 if (case_block.wantSafety()) {
11002 try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
11003 try sema.safetyPanic(&case_block, src, .corrupt_switch);
11004 } else {
11005 _ = try case_block.addNoOp(.unreach);
11006 }
11007 break :hint .cold; // Safety check / unreachable branches are cold.
11008 };
11009 try branch_hints.append(gpa, catch_all_hint);
11010 break :catch_all_extra @ptrCast(case_block.instructions.items);
11011 };
11012
11013 assert(branch_hints.count == cases_len + 1); // +1 for catch-all hint
11014
11015 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len +
11016 branch_hints.bags.items.len +
11017 cases_extra.items.len +
11018 catch_all_extra.len);
11019 const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{
11020 .cases_len = @intCast(cases_len),
11021 .else_body_len = @intCast(catch_all_extra.len),
11022 });
11023 sema.air_extra.appendSliceAssumeCapacity(branch_hints.bags.items);
11024 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
11025 sema.air_extra.appendSliceAssumeCapacity(catch_all_extra);
11026
11027 const air_tag: Air.Inst.Tag = if (operand == .loop and merges.extra_insts.items.len > 0)
11028 .loop_switch_br
11029 else
11030 .switch_br;
11031 const air_ref = try child_block.addInst(.{
11032 .tag = air_tag,
11033 .data = .{ .pl_op = .{
11034 .operand = cond_ref,
11035 .payload = payload_index,
11036 } },
11037 });
11038 return air_ref;
11039}
11040
11041const ValidatedSwitchBlock = struct {
11042 seen: Seen,
11043 case_vals: []const Air.Inst.Ref,
11044 else_case: Zir.UnwrappedSwitchBlock.Case.Else,
11045 else_err_ty: ?Type,
11046
11047 const Seen = struct {
11048 enum_fields: []?LazySrcLoc,
11049 errors: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc),
11050 sparse_values: std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc),
11051 ranges: RangeSet,
11052 true_src: ?LazySrcLoc,
11053 false_src: ?LazySrcLoc,
11054 void_src: ?LazySrcLoc,
11055 };
11056
11057 fn iterateUnhandledItems(
11058 validated_switch: *const ValidatedSwitchBlock,
11059 /// May be `undefined` if `item_ty` isn't an `error_set`.
11060 error_names: InternPool.NullTerminatedString.Slice,
11061 /// May be `undefined` if `item_ty` isn't an `int`.
11062 min_int: Value,
11063 ) UnhandledIterator {
11064 return .{
11065 .error_names = error_names,
11066 .seen = &validated_switch.seen,
11067
11068 .next_idx = 0,
11069 .next_val = min_int,
11070 .handled_true = validated_switch.seen.true_src != null,
11071 .handled_false = validated_switch.seen.false_src != null,
11072 .handled_void = validated_switch.seen.void_src != null,
11073 };
11074 }
11075
11076 const UnhandledIterator = struct {
11077 error_names: InternPool.NullTerminatedString.Slice,
11078 seen: *const Seen,
11079
11080 next_idx: u32,
11081 next_val: ?Value,
11082 handled_true: bool,
11083 handled_false: bool,
11084 handled_void: bool,
11085
11086 fn next(it: *UnhandledIterator, sema: *Sema, item_ty: Type) CompileError!?Value {
11087 const pt = sema.pt;
11088 const zcu = pt.zcu;
11089 const ip = &zcu.intern_pool;
11090 switch (item_ty.zigTypeTag(zcu)) {
11091 .@"enum" => {
11092 for (it.seen.enum_fields[it.next_idx..], it.next_idx..) |seen_field, field_i| {
11093 if (seen_field != null) continue;
11094 it.next_idx = @intCast(field_i + 1);
11095 return try pt.enumValueFieldIndex(item_ty, @intCast(field_i));
11096 }
11097 return null;
11098 },
11099 .error_set => {
11100 for (it.error_names.get(ip)[it.next_idx..], it.next_idx..) |err_name, name_i| {
11101 if (it.seen.errors.contains(err_name)) continue;
11102 it.next_idx = @intCast(name_i + 1);
11103 return .fromInterned(try pt.intern(.{ .err = .{
11104 .ty = item_ty.toIntern(),
11105 .name = err_name,
11106 } }));
11107 }
11108 return null;
11109 },
11110 .int, .@"union", .@"struct" => |type_tag| {
11111 var cur_val = it.next_val orelse return null;
11112 const int_ty = switch (type_tag) {
11113 .int => item_ty,
11114 .@"union", .@"struct" => item_ty.backingIntType(zcu),
11115 else => unreachable,
11116 };
11117 while (it.next_idx < it.seen.ranges.list.len and
11118 cur_val.eql(it.seen.ranges.list.items(.first)[it.next_idx], int_ty, zcu))
11119 {
11120 defer it.next_idx += 1;
11121 const incr = try arith.incrementDefinedInt(
11122 sema,
11123 int_ty,
11124 it.seen.ranges.list.items(.last)[it.next_idx],
11125 );
11126 if (incr.overflow) {
11127 it.next_val = null;
11128 return null;
11129 }
11130 cur_val = incr.val;
11131 }
11132 const incr = try arith.incrementDefinedInt(sema, int_ty, cur_val);
11133 it.next_val = if (incr.overflow) null else incr.val;
11134 return switch (type_tag) {
11135 .int => cur_val,
11136 .@"union", .@"struct" => try pt.bitpackValue(item_ty, cur_val),
11137 else => unreachable,
11138 };
11139 },
11140 .bool => {
11141 if (!it.handled_true) {
11142 it.handled_true = true;
11143 return .true;
11144 }
11145 if (!it.handled_false) {
11146 it.handled_false = true;
11147 return .false;
11148 }
11149 return null;
11150 },
11151 .void => {
11152 if (!it.handled_void) {
11153 it.handled_void = true;
11154 return .void;
11155 }
11156 return null;
11157 },
11158 else => unreachable, // item type is not enumerable
11159 }
11160 }
11161 };
11162};
11163
11164/// Validates operand type and `else`/`_` prong usage, resolves all prong items
11165/// and checks them for duplicates/invalid ranges. Does not emit into `block`.
11166/// Reserves inst map space for all placeholders associated with `zir_switch`.
11167/// Contents of returned `ValidatedSwitchBlock` belong to `sema.arena`.
11168fn validateSwitchBlock(
11169 sema: *Sema,
11170 block: *Block,
11171 raw_operand: Air.Inst.Ref,
11172 operand_is_ref: bool,
11173 switch_inst: Zir.Inst.Index,
11174 zir_switch: *const Zir.UnwrappedSwitchBlock,
11175) CompileError!ValidatedSwitchBlock {
11176 const pt = sema.pt;
11177 const zcu = pt.zcu;
11178 const ip = &zcu.intern_pool;
11179 const gpa = sema.gpa;
11180 const arena = sema.arena;
11181
11182 const src_node_offset = zir_switch.switch_src_node_offset;
11183 const src = block.nodeOffset(src_node_offset);
11184 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
11185 const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset });
11186 var extra_index = zir_switch.end;
11187
11188 // We want to map values to our placeholders later on.
11189 if (zir_switch.payload_capture_placeholder.unwrap()) |payload_capture_inst| {
11190 assert(payload_capture_inst != switch_inst); // malformed zir
11191 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{payload_capture_inst});
11192 }
11193 if (zir_switch.tag_capture_placeholder.unwrap()) |tag_capture_inst| {
11194 assert(tag_capture_inst != switch_inst); // malformed zir
11195 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst});
11196 }
11197
11198 const operand_ty = operand_ty: {
11199 const raw_operand_ty = sema.typeOf(raw_operand);
11200 if (operand_is_ref) {
11201 try sema.checkPtrType(block, operand_src, raw_operand_ty, false);
11202 const child_ty = raw_operand_ty.childType(zcu);
11203 try sema.ensureLayoutResolved(child_ty, operand_src, .ptr_access);
11204 break :operand_ty child_ty;
11205 }
11206 break :operand_ty raw_operand_ty;
11207 };
11208
11209 const item_ty: Type = item_ty: {
11210 switch (operand_ty.zigTypeTag(zcu)) {
11211 .@"enum",
11212 .error_set,
11213 .int,
11214 .comptime_int,
11215 .type,
11216 .enum_literal,
11217 .@"fn",
11218 .bool,
11219 .void,
11220 => break :item_ty operand_ty,
11221
11222 .@"union" => {
11223 operand_ty.assertHasLayout(zcu);
11224 const union_obj = ip.loadUnionType(operand_ty.toIntern());
11225 switch (union_obj.tag_usage) {
11226 .tagged => break :item_ty .fromInterned(union_obj.enum_tag_type),
11227 .none => if (union_obj.layout == .@"packed") break :item_ty operand_ty,
11228 .safety => {},
11229 }
11230 return sema.failWithOwnedErrorMsg(block, msg: {
11231 const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{});
11232 errdefer msg.destroy(sema.gpa);
11233 if (operand_ty.srcLocOrNull(zcu)) |union_src| {
11234 try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{});
11235 }
11236 break :msg msg;
11237 });
11238 },
11239
11240 .@"struct" => {
11241 operand_ty.assertHasLayout(zcu);
11242 if (operand_ty.containerLayout(zcu) == .@"packed") break :item_ty operand_ty;
11243 return sema.failWithOwnedErrorMsg(block, msg: {
11244 const msg = try sema.errMsg(operand_src, "switch on non-packed struct", .{});
11245 errdefer msg.destroy(sema.gpa);
11246 try sema.addDeclaredHereNote(msg, operand_ty);
11247 break :msg msg;
11248 });
11249 },
11250
11251 .pointer => if (!operand_ty.isSlice(zcu)) break :item_ty operand_ty,
11252
11253 .optional => return sema.failWithOwnedErrorMsg(block, msg: {
11254 const msg = try sema.errMsg(operand_src, "switch on optional type '{f}'", .{
11255 operand_ty.fmt(pt),
11256 });
11257 errdefer msg.destroy(gpa);
11258 try sema.errNote(operand_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
11259 break :msg msg;
11260 }),
11261
11262 .error_union => return sema.failWithOwnedErrorMsg(block, msg: {
11263 const msg = try sema.errMsg(operand_src, "switch on error union type '{f}'", .{
11264 operand_ty.fmt(pt),
11265 });
11266 errdefer msg.destroy(gpa);
11267 try sema.errNote(operand_src, msg, "consider using 'try', 'catch', or 'if'", .{});
11268 break :msg msg;
11269 }),
11270
11271 .noreturn,
11272 .float,
11273 .comptime_float,
11274 .array,
11275 .vector,
11276 .undefined,
11277 .null,
11278 .@"opaque",
11279 .frame,
11280 .@"anyframe",
11281 .spirv,
11282 => {},
11283 }
11284 return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)});
11285 };
11286
11287 if (zir_switch.has_continue and !block.isComptime()) {
11288 if (operand_ty.comptimeOnly(zcu)) {
11289 // Even if the operand is comptime-known, this `switch` is runtime.
11290 return sema.failWithOwnedErrorMsg(block, msg: {
11291 const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)});
11292 errdefer msg.destroy(gpa);
11293 try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{});
11294 try sema.explainWhyTypeIsComptime(msg, operand_src, operand_ty);
11295 break :msg msg;
11296 });
11297 }
11298 try sema.validateRuntimeValue(block, operand_src, raw_operand);
11299 }
11300
11301 const has_else = zir_switch.else_case != null;
11302 const has_under = zir_switch.has_under;
11303
11304 var case_vals: std.ArrayList(Air.Inst.Ref) = try .initCapacity(arena, zir_switch.item_infos.len);
11305
11306 // Duplicate checking variables later also used for `inline else`.
11307 var seen: ValidatedSwitchBlock.Seen = .{
11308 .enum_fields = &.{},
11309 .errors = .empty,
11310 .sparse_values = .empty,
11311 .ranges = .empty,
11312 .true_src = null,
11313 .false_src = null,
11314 .void_src = null,
11315 };
11316
11317 var else_err_ty: ?Type = null;
11318
11319 const else_case = zir_switch.else_case orelse undefined;
11320
11321 switch (item_ty.zigTypeTag(zcu)) {
11322 .@"enum" => {
11323 seen.enum_fields = try arena.alloc(?LazySrcLoc, item_ty.enumFieldCount(zcu));
11324 @memset(seen.enum_fields, null);
11325 // `seen.ranges` is used for non-exhaustive enum values that do not
11326 // correspond to any tags. Since this is rare, we only allocate on
11327 // demand in `validateSwitchItem`.
11328 },
11329 .error_set => {
11330 try seen.errors.ensureUnusedCapacity(arena, zir_switch.totalItemsLen());
11331 },
11332 .int, .comptime_int, .@"union", .@"struct" => {
11333 try seen.ranges.ensureUnusedCapacity(arena, zir_switch.totalItemsLen());
11334 },
11335 .enum_literal, .@"fn", .pointer, .type => {
11336 try seen.sparse_values.ensureUnusedCapacity(arena, zir_switch.totalItemsLen());
11337 },
11338 .bool, .void => {},
11339
11340 else => unreachable,
11341 }
11342
11343 // Validate for duplicate items and invalid ranges.
11344 var case_it = zir_switch.iterateCases();
11345 while (case_it.next()) |case| {
11346 const prong_info = case.prong_info;
11347 extra_index += prong_info.body_len;
11348 for (case.item_infos, 0..) |item_info, item_i| {
11349 const item_src = block.src(.{ .switch_case_item = .{
11350 .switch_node_offset = src_node_offset,
11351 .case_idx = case.index,
11352 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
11353 } });
11354 if (item_info.unwrap() == .under) {
11355 if (!operand_ty.isNonexhaustiveEnum(zcu)) return sema.failWithOwnedErrorMsg(block, msg: {
11356 const msg = try sema.errMsg(
11357 src,
11358 "'_' prong only allowed when switching on non-exhaustive enums",
11359 .{},
11360 );
11361 errdefer msg.destroy(gpa);
11362 try sema.errNote(
11363 item_src,
11364 msg,
11365 "'_' prong here",
11366 .{},
11367 );
11368 try sema.errNote(
11369 src,
11370 msg,
11371 "consider using 'else'",
11372 .{},
11373 );
11374 break :msg msg;
11375 });
11376 case_vals.appendAssumeCapacity(.none);
11377 } else {
11378 const item, extra_index = try sema.resolveSwitchItem(block, item_src, item_ty, item_info, extra_index, switch_inst, prong_info.is_comptime_unreach);
11379 try sema.validateSwitchItemOrRange(block, item_src, item.val, null, item_ty, &seen);
11380 case_vals.appendAssumeCapacity(item.ref);
11381 }
11382 }
11383 for (case.range_infos, 0..) |range_info, range_i| {
11384 const range_offset: LazySrcLoc.Offset.SwitchItem = .{
11385 .switch_node_offset = src_node_offset,
11386 .case_idx = case.index,
11387 .item_idx = .{ .kind = .range, .value = @intCast(range_i) },
11388 };
11389 const range_src = block.src(.{ .switch_case_item = range_offset });
11390 const first_src = block.src(.{ .switch_case_item_range_first = range_offset });
11391 const last_src = block.src(.{ .switch_case_item_range_last = range_offset });
11392 const first_item, extra_index = try sema.resolveSwitchItem(block, first_src, item_ty, range_info[0], extra_index, switch_inst, prong_info.is_comptime_unreach);
11393 const last_item, extra_index = try sema.resolveSwitchItem(block, last_src, item_ty, range_info[1], extra_index, switch_inst, prong_info.is_comptime_unreach);
11394 try sema.validateSwitchItemOrRange(block, range_src, first_item.val, last_item.val, item_ty, &seen);
11395 case_vals.appendSliceAssumeCapacity(&.{ first_item.ref, last_item.ref });
11396 }
11397 }
11398
11399 switch (item_ty.zigTypeTag(zcu)) {
11400 .int, .comptime_int => {},
11401 else => if (zir_switch.anyRanges()) {
11402 const range_src = block.src(.{ .node_offset_switch_range = src_node_offset });
11403 const msg = msg: {
11404 const msg = try sema.errMsg(
11405 operand_src,
11406 "ranges not allowed when switching on type '{f}'",
11407 .{operand_ty.fmt(pt)},
11408 );
11409 errdefer msg.destroy(gpa);
11410 try sema.errNote(
11411 range_src,
11412 msg,
11413 "range here",
11414 .{},
11415 );
11416 break :msg msg;
11417 };
11418 return sema.failWithOwnedErrorMsg(block, msg);
11419 },
11420 }
11421
11422 // Validate for missing special prongs.
11423 switch (item_ty.zigTypeTag(zcu)) {
11424 .@"enum" => {
11425 const all_tags_handled = for (seen.enum_fields) |seen_src| {
11426 if (seen_src == null) break false;
11427 } else true;
11428
11429 if (has_else) {
11430 if (all_tags_handled) {
11431 if (operand_ty.isNonexhaustiveEnum(zcu)) {
11432 if (has_under) return sema.fail(
11433 block,
11434 else_prong_src,
11435 "unreachable else prong; all explicit cases already handled",
11436 .{},
11437 );
11438 } else return sema.fail(
11439 block,
11440 else_prong_src,
11441 "unreachable else prong; all cases already handled",
11442 .{},
11443 );
11444 }
11445 } else if (!all_tags_handled) {
11446 const msg = msg: {
11447 const msg = try sema.errMsg(
11448 src,
11449 "switch must handle all possibilities",
11450 .{},
11451 );
11452 errdefer msg.destroy(sema.gpa);
11453 for (seen.enum_fields, 0..) |seen_src, i| {
11454 if (seen_src != null) continue;
11455
11456 const field_name = item_ty.enumFieldName(i, zcu);
11457 try sema.addFieldErrNote(
11458 item_ty,
11459 i,
11460 msg,
11461 "unhandled enumeration value: '{f}'",
11462 .{field_name.fmt(ip)},
11463 );
11464 }
11465 try sema.errNote(
11466 item_ty.srcLoc(zcu),
11467 msg,
11468 "enum '{f}' declared here",
11469 .{item_ty.fmt(pt)},
11470 );
11471 break :msg msg;
11472 };
11473 return sema.failWithOwnedErrorMsg(block, msg);
11474 } else if (!has_else and !has_under and
11475 item_ty.isNonexhaustiveEnum(zcu) and operand_ty.zigTypeTag(zcu) != .@"union")
11476 {
11477 return sema.fail(
11478 block,
11479 src,
11480 "switch on non-exhaustive enum must include 'else' or '_' prong or both",
11481 .{},
11482 );
11483 }
11484 },
11485 .error_set => {
11486 else_err_ty = ty: switch (try sema.resolveInferredErrorSetTy(block, src, item_ty.toIntern())) {
11487 .anyerror_type => {
11488 if (!has_else) {
11489 return sema.fail(
11490 block,
11491 src,
11492 "else prong required when switching on type 'anyerror'",
11493 .{},
11494 );
11495 }
11496 break :ty .anyerror;
11497 },
11498 else => |err_set_ty_index| {
11499 const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names;
11500 var maybe_msg: ?*Zcu.ErrorMsg = null;
11501 errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa);
11502
11503 var seen_errors_from_set: u32 = 0;
11504 for (error_names.get(ip)) |error_name| {
11505 if (seen.errors.contains(error_name)) {
11506 seen_errors_from_set += 1;
11507 } else if (!has_else) {
11508 const msg = maybe_msg orelse blk: {
11509 maybe_msg = try sema.errMsg(
11510 src,
11511 "switch must handle all possibilities",
11512 .{},
11513 );
11514 break :blk maybe_msg.?;
11515 };
11516
11517 try sema.errNote(
11518 src,
11519 msg,
11520 "unhandled error value: 'error.{f}'",
11521 .{error_name.fmt(ip)},
11522 );
11523 }
11524 }
11525
11526 if (maybe_msg) |msg| {
11527 maybe_msg = null;
11528 try sema.addDeclaredHereNote(msg, operand_ty);
11529 return sema.failWithOwnedErrorMsg(block, msg);
11530 }
11531
11532 if (has_else and seen_errors_from_set == error_names.len) {
11533 // This prong is unreachable anyway so we don't need its
11534 // error set type, but we still allow it to exist.
11535 if (else_case.is_simple_noreturn) break :ty null;
11536 return sema.fail(
11537 block,
11538 else_prong_src,
11539 "unreachable else prong; all cases already handled",
11540 .{},
11541 );
11542 }
11543
11544 var names: InferredErrorSet.NameMap = .{};
11545 try names.ensureUnusedCapacity(sema.arena, error_names.len);
11546 for (error_names.get(ip)) |error_name| {
11547 if (seen.errors.contains(error_name)) continue;
11548 names.putAssumeCapacityNoClobber(error_name, {});
11549 }
11550 // No need to keep the hash map metadata correct; here we
11551 // extract the (sorted) keys only.
11552 break :ty try pt.errorSetFromUnsortedNames(names.keys());
11553 },
11554 };
11555 },
11556 .int, .@"union", .@"struct" => |type_tag| {
11557 check_range: {
11558 const int_ty = switch (type_tag) {
11559 .int => item_ty,
11560 .@"union", .@"struct" => item_ty.backingIntType(zcu),
11561 else => unreachable,
11562 };
11563 const min_int = try int_ty.minInt(pt, int_ty);
11564 const max_int = try int_ty.maxInt(pt, int_ty);
11565 if (try seen.ranges.spans(arena, min_int, max_int, int_ty, zcu)) {
11566 if (has_else) {
11567 return sema.fail(
11568 block,
11569 else_prong_src,
11570 "unreachable else prong; all cases already handled",
11571 .{},
11572 );
11573 }
11574 break :check_range;
11575 }
11576 if (!has_else) {
11577 return sema.fail(
11578 block,
11579 src,
11580 "switch must handle all possibilities",
11581 .{},
11582 );
11583 }
11584 }
11585 },
11586 .comptime_int, .enum_literal, .@"fn", .pointer, .type => {
11587 if (!has_else) {
11588 return sema.fail(
11589 block,
11590 src,
11591 "else prong required when switching on type '{f}'",
11592 .{item_ty.fmt(pt)},
11593 );
11594 }
11595 },
11596 .bool, .void => |type_tag| {
11597 const all_values_handled = switch (type_tag) {
11598 .bool => seen.true_src != null and seen.false_src != null,
11599 .void => seen.void_src != null,
11600 else => unreachable,
11601 };
11602 if (has_else) {
11603 if (all_values_handled) {
11604 return sema.fail(
11605 block,
11606 else_prong_src,
11607 "unreachable else prong; all cases already handled",
11608 .{},
11609 );
11610 }
11611 } else {
11612 if (!all_values_handled) {
11613 return sema.fail(
11614 block,
11615 src,
11616 "switch must handle all possibilities",
11617 .{},
11618 );
11619 }
11620 }
11621 },
11622 else => unreachable,
11623 }
11624
11625 return .{
11626 .seen = seen,
11627 .case_vals = case_vals.items,
11628 .else_case = else_case,
11629 .else_err_ty = else_err_ty,
11630 };
11631}
11632
11633fn resolveSwitchBlock(
11634 sema: *Sema,
11635 block: *Block,
11636 child_block: *Block,
11637 operand: SwitchOperand,
11638 raw_operand_ty: Type,
11639 cond_val: Value,
11640 merges: *Block.Merges,
11641 switch_inst: Zir.Inst.Index,
11642 zir_switch: *const Zir.UnwrappedSwitchBlock,
11643 validated_switch: *const ValidatedSwitchBlock,
11644) CompileError!Air.Inst.Ref {
11645 const pt = sema.pt;
11646 const zcu = pt.zcu;
11647
11648 const switch_node_offset = zir_switch.switch_src_node_offset;
11649
11650 const operand_ty = sema.typeOf(operand.simple.by_val);
11651
11652 const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and
11653 operand_ty.containerLayout(zcu) != .@"packed";
11654 const err_set = operand_ty.zigTypeTag(zcu) == .error_set;
11655
11656 const item_ty = if (tagged_union_originally)
11657 operand_ty.unionTagType(zcu).?
11658 else
11659 operand_ty;
11660
11661 const cond_ref = operand.simple.cond;
11662
11663 const case_vals = validated_switch.case_vals;
11664 var case_val_idx: usize = 0;
11665 var extra_index = zir_switch.end;
11666 var case_it = zir_switch.iterateCases();
11667 var under_prong: ?struct {
11668 index: Zir.UnwrappedSwitchBlock.Case.Index,
11669 body: []const Zir.Inst.Index,
11670 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
11671 has_tag_capture: bool,
11672 } = null;
11673 while (case_it.next()) |case| {
11674 const prong_info = case.prong_info;
11675 const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len);
11676 extra_index += prong_body.len;
11677 for (case.item_infos) |item_info| {
11678 if (item_info.bodyLen()) |body_len| extra_index += body_len;
11679 }
11680 for (case.range_infos) |range_info| {
11681 if (range_info[0].bodyLen()) |body_len| extra_index += body_len;
11682 if (range_info[1].bodyLen()) |body_len| extra_index += body_len;
11683 }
11684
11685 const item_refs = case_vals[case_val_idx..][0..case.item_infos.len];
11686 case_val_idx += item_refs.len;
11687 const range_refs: []const [2]Air.Inst.Ref = @ptrCast(case_vals[case_val_idx..][0 .. 2 * case.range_infos.len]);
11688 case_val_idx += 2 * range_refs.len;
11689 for (item_refs) |item_ref| {
11690 if (item_ref == .none) {
11691 under_prong = .{
11692 .index = case.index,
11693 .body = prong_body,
11694 .capture = case.prong_info.capture,
11695 .has_tag_capture = case.prong_info.has_tag_capture,
11696 };
11697 continue;
11698 }
11699 const item_val = sema.resolveValue(item_ref).?;
11700 if (cond_val.eql(item_val, item_ty, zcu)) {
11701 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, prong_body, cond_ref);
11702 if (tagged_union_originally and operand_ty.unionFieldType(item_val, zcu).?.isNoReturn(zcu)) {
11703 // This prong should be unreachable!
11704 return .unreachable_value;
11705 }
11706 const prong_items: SwitchProngItems = prong_items: {
11707 if (prong_info.is_inline) break :prong_items .{ .@"inline" = cond_ref };
11708 if (range_refs.len > 0) break :prong_items .has_ranges;
11709 break :prong_items .{ .item_refs = item_refs };
11710 };
11711 return sema.resolveSwitchProng(
11712 block,
11713 child_block,
11714 operand,
11715 raw_operand_ty,
11716 prong_body,
11717 block.src(.{ .switch_capture = .{
11718 .switch_node_offset = switch_node_offset,
11719 .case_idx = case.index,
11720 } }),
11721 prong_info.capture,
11722 prong_info.has_tag_capture,
11723 prong_items,
11724 validated_switch.else_err_ty,
11725 merges,
11726 switch_inst,
11727 zir_switch,
11728 );
11729 }
11730 }
11731 for (range_refs) |range_ref| {
11732 const first_val = sema.resolveValue(range_ref[0]).?;
11733 const last_val = sema.resolveValue(range_ref[1]).?;
11734 if ((try sema.compareAll(cond_val, .gte, first_val, item_ty)) and
11735 (try sema.compareAll(cond_val, .lte, last_val, item_ty)))
11736 {
11737 const prong_items: SwitchProngItems = if (prong_info.is_inline)
11738 .{ .@"inline" = cond_ref }
11739 else
11740 .has_ranges;
11741 return sema.resolveSwitchProng(
11742 block,
11743 child_block,
11744 operand,
11745 raw_operand_ty,
11746 prong_body,
11747 block.src(.{ .switch_capture = .{
11748 .switch_node_offset = switch_node_offset,
11749 .case_idx = case.index,
11750 } }),
11751 prong_info.capture,
11752 prong_info.has_tag_capture,
11753 prong_items,
11754 validated_switch.else_err_ty,
11755 merges,
11756 switch_inst,
11757 zir_switch,
11758 );
11759 }
11760 }
11761 }
11762
11763 assert(zir_switch.else_case != null or under_prong != null); // switch exhaustion check wrong
11764
11765 const else_case = validated_switch.else_case;
11766 const else_is_named_only = zir_switch.else_case != null and under_prong != null;
11767
11768 // named-only prong
11769
11770 if (else_is_named_only and item_ty.enumTagFieldIndex(cond_val, zcu) != null) {
11771 assert(item_ty.isNonexhaustiveEnum(zcu));
11772 const prong_items: SwitchProngItems = if (else_case.is_inline)
11773 .{ .@"inline" = cond_ref }
11774 else
11775 .special;
11776 return sema.resolveSwitchProng(
11777 block,
11778 child_block,
11779 operand,
11780 raw_operand_ty,
11781 else_case.body,
11782 block.src(.{ .switch_capture = .{
11783 .switch_node_offset = switch_node_offset,
11784 .case_idx = else_case.index,
11785 } }),
11786 else_case.capture,
11787 else_case.has_tag_capture,
11788 prong_items,
11789 validated_switch.else_err_ty,
11790 merges,
11791 switch_inst,
11792 zir_switch,
11793 );
11794 }
11795
11796 // catch-all prong
11797
11798 const index, const body, const capture, const has_tag_capture, const is_inline = if (under_prong) |under|
11799 .{ under.index, under.body, under.capture, under.has_tag_capture, false }
11800 else
11801 .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline };
11802 if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_ref);
11803 if (tagged_union_originally) {
11804 for (validated_switch.seen.enum_fields, 0..) |maybe_seen, field_i| {
11805 if (maybe_seen != null) continue;
11806 if (!operand_ty.unionFieldTypeByIndex(field_i, zcu).isNoReturn(zcu)) break;
11807 } else {
11808 // This prong should be unreachable!
11809 return .unreachable_value;
11810 }
11811 }
11812 const prong_items: SwitchProngItems = if (is_inline)
11813 .{ .@"inline" = cond_ref }
11814 else
11815 .special;
11816 return sema.resolveSwitchProng(
11817 block,
11818 child_block,
11819 operand,
11820 raw_operand_ty,
11821 body,
11822 block.src(.{ .switch_capture = .{
11823 .switch_node_offset = switch_node_offset,
11824 .case_idx = index,
11825 } }),
11826 capture,
11827 has_tag_capture,
11828 prong_items,
11829 validated_switch.else_err_ty,
11830 merges,
11831 switch_inst,
11832 zir_switch,
11833 );
11834}
11835
11836const SwitchOperand = union(enum) {
11837 /// This switch will be dispatched only once, with the given operand.
11838 simple: struct {
11839 /// The raw switch operand value. Always defined.
11840 by_val: Air.Inst.Ref,
11841 /// The switch operand *pointer*. `none` if there are no prongs with a
11842 /// by-ref capture.
11843 by_ref: Air.Inst.Ref,
11844 /// The switch condition value. For unions, `operand` is the union
11845 /// and `cond` is its enum tag value.
11846 cond: Air.Inst.Ref,
11847 },
11848 /// This switch may be dispatched multiple times with `continue` syntax.
11849 /// As such, the operand is stored in an alloc if needed.
11850 loop: struct {
11851 /// The `alloc` containing the `switch` operand for the active dispatch.
11852 /// Each prong must load from this `alloc` to get captures.
11853 /// If there are no captures, this may be `none`.
11854 operand_alloc: Air.Inst.Ref,
11855 /// Whether `operand_alloc` contains a by-val operand or a by-ref
11856 /// operand.
11857 operand_is_ref: bool,
11858 /// The switch condition value for the *initial* dispatch. For
11859 /// unions, this is the enum tag value.
11860 init_cond: Air.Inst.Ref,
11861 },
11862};
11863
11864fn analyzeSwitchOperandLoad(
11865 sema: *Sema,
11866 block: *Block,
11867 operand: SwitchOperand,
11868 operand_src: LazySrcLoc,
11869 by_ref: bool,
11870) CompileError!Air.Inst.Ref {
11871 switch (operand) {
11872 .simple => |s| {
11873 if (by_ref) {
11874 assert(s.by_ref != .none);
11875 return s.by_ref;
11876 } else {
11877 return s.by_val;
11878 }
11879 },
11880 .loop => |l| {
11881 const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src);
11882 assert(loaded != .none); // there are no captures, so no need to load the switch operand
11883 if (by_ref) {
11884 assert(l.operand_is_ref);
11885 return loaded;
11886 }
11887 return if (l.operand_is_ref)
11888 try sema.analyzeLoad(block, operand_src, loaded, operand_src)
11889 else
11890 loaded;
11891 },
11892 }
11893}
11894
11895const SwitchProngItems = union(enum) {
11896 @"inline": Air.Inst.Ref,
11897 item_refs: []const Air.Inst.Ref,
11898 has_ranges,
11899 special,
11900};
11901
11902/// A switch capture is comptime-known if it is `inline` and/or it is a by-value
11903/// capture of a prong with a single item.
11904fn resolveSwitchCaptureFromProngItems(
11905 sema: *Sema,
11906 prong_items: SwitchProngItems,
11907 by_ref: bool,
11908) ?Value {
11909 const ref: Air.Inst.Ref = switch (prong_items) {
11910 .@"inline" => |ref| ref,
11911 .item_refs => |refs| if (refs.len == 1 and !by_ref) refs[0] else return null,
11912 .has_ranges, .special => return null,
11913 };
11914 return sema.resolveValue(ref).?;
11915}
11916
11917/// Resolve a switch prong which is determined at comptime to have no peers.
11918/// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`.
11919fn resolveSwitchProng(
11920 sema: *Sema,
11921 block: *Block,
11922 child_block: *Block,
11923 operand: SwitchOperand,
11924 raw_operand_ty: Type,
11925 prong_body: []const Zir.Inst.Index,
11926 /// Must use the `switch_capture` field in `offset`.
11927 capture_src: LazySrcLoc,
11928 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
11929 has_tag_capture: bool,
11930 prong_items: SwitchProngItems,
11931 else_err_ty: ?Type,
11932 merges: *Block.Merges,
11933 switch_inst: Zir.Inst.Index,
11934 zir_switch: *const Zir.UnwrappedSwitchBlock,
11935) CompileError!Air.Inst.Ref {
11936 const src_node_offset = zir_switch.switch_src_node_offset;
11937 const src = block.nodeOffset(src_node_offset);
11938 const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset });
11939
11940 // We can propagate `.cold` hints from this branch since it's comptime-known
11941 // to be taken from the parent branch.
11942 const parent_hint = sema.branch_hint;
11943 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
11944
11945 const analyzed_captures = try sema.analyzeSwitchCaptures(
11946 child_block,
11947 operand,
11948 operand_src,
11949 sema.typeOf(operand.simple.by_val),
11950 capture_src,
11951 capture,
11952 has_tag_capture,
11953 prong_items,
11954 else_err_ty,
11955 );
11956
11957 const payload_inst = if (capture != .none) inst: {
11958 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
11959 sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref);
11960 break :inst payload_inst;
11961 } else undefined;
11962 defer if (capture != .none) assert(sema.inst_map.remove(payload_inst));
11963
11964 const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: {
11965 const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
11966 sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref);
11967 break :inst tag_inst;
11968 } else undefined;
11969 defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst));
11970
11971 if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty));
11972 defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst));
11973
11974 return sema.resolveBlockBody(block, src, child_block, prong_body, switch_inst, merges);
11975}
11976
11977fn wantSwitchProngBodyAnalysis(
11978 sema: *Sema,
11979 item_ref: Air.Inst.Ref,
11980 operand_ty: Type,
11981 tagged_union_originally: bool,
11982 err_set: bool,
11983 prong_is_comptime_unreach: bool,
11984) bool {
11985 const zcu = sema.pt.zcu;
11986 if (tagged_union_originally) {
11987 const item_val = sema.resolveValue(item_ref).?;
11988 const field_ty = operand_ty.unionFieldType(item_val, zcu).?;
11989 if (field_ty.isNoReturn(zcu)) return false;
11990 }
11991 if (err_set and prong_is_comptime_unreach) {
11992 const item_val = sema.resolveValue(item_ref).?;
11993 const err_name = item_val.getErrorName(zcu).unwrap().?;
11994 if (!operand_ty.errorSetHasField(err_name, zcu)) return false;
11995 }
11996 return true;
11997}
11998
11999/// Assumes that `operand_ty` has more than one possible value.
12000/// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`.
12001fn analyzeSwitchProng(
12002 sema: *Sema,
12003 case_block: *Block,
12004 operand: SwitchOperand,
12005 operand_ty: Type,
12006 raw_operand_ty: Type,
12007 prong_body: []const Zir.Inst.Index,
12008 /// Must use the `switch_capture` field in `offset`.
12009 capture_src: LazySrcLoc,
12010 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12011 has_tag_capture: bool,
12012 prong_items: SwitchProngItems,
12013 else_err_ty: ?Type,
12014 switch_inst: Zir.Inst.Index,
12015 zir_switch: *const Zir.UnwrappedSwitchBlock,
12016) CompileError!std.lang.BranchHint {
12017 const pt = sema.pt;
12018 const zcu = pt.zcu;
12019
12020 const operand_src = case_block.src(.{ .node_offset_switch_operand = zir_switch.switch_src_node_offset });
12021
12022 if (operand_ty.zigTypeTag(zcu) == .error_set) {
12023 const cond_ref = switch (operand) {
12024 .simple => |s| s.cond,
12025 .loop => |l| l.init_cond,
12026 };
12027 if (try sema.maybeErrorUnwrap(case_block, prong_body, cond_ref, operand_src, true)) {
12028 // nothing to do here. weight against error branch
12029 return .unlikely;
12030 }
12031 }
12032
12033 const analyzed_captures = try sema.analyzeSwitchCaptures(
12034 case_block,
12035 operand,
12036 operand_src,
12037 operand_ty,
12038 capture_src,
12039 capture,
12040 has_tag_capture,
12041 prong_items,
12042 else_err_ty,
12043 );
12044
12045 const payload_inst = if (capture != .none) inst: {
12046 const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst;
12047 sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref);
12048 break :inst payload_inst;
12049 } else undefined;
12050 defer if (capture != .none) assert(sema.inst_map.remove(payload_inst));
12051
12052 const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: {
12053 const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst;
12054 sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref);
12055 break :inst tag_inst;
12056 } else undefined;
12057 defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst));
12058
12059 if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty));
12060 defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst));
12061
12062 return sema.analyzeBodyRuntimeBreak(case_block, prong_body);
12063}
12064
12065fn analyzeSwitchCaptures(
12066 sema: *Sema,
12067 case_block: *Block,
12068 operand: SwitchOperand,
12069 operand_src: LazySrcLoc,
12070 operand_ty: Type,
12071 capture_src: LazySrcLoc,
12072 capture: Zir.Inst.SwitchBlock.ProngInfo.Capture,
12073 has_tag_capture: bool,
12074 prong_items: SwitchProngItems,
12075 else_err_ty: ?Type,
12076) CompileError!struct {
12077 payload_ref: Air.Inst.Ref,
12078 tag_ref: Air.Inst.Ref,
12079} {
12080 const pt = sema.pt;
12081 const zcu = pt.zcu;
12082
12083 if (operand_ty.zigTypeTag(zcu) == .@"union" and
12084 operand_ty.containerLayout(zcu) != .@"packed")
12085 {
12086 if (capture == .none) {
12087 const tag_ref: Air.Inst.Ref = tag_ref: {
12088 if (!has_tag_capture) break :tag_ref .none;
12089 if (sema.resolveSwitchCaptureFromProngItems(prong_items, false)) |tag_val| {
12090 break :tag_ref .fromValue(tag_val);
12091 }
12092 const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, false);
12093 break :tag_ref try sema.unionToTag(case_block, loaded_operand);
12094 };
12095 return .{ .payload_ref = .none, .tag_ref = tag_ref };
12096 }
12097
12098 // We always have to load the operand for tagged union payload captures
12099 // since we can't derive the payload value from the tag (except for OPV
12100 // types, for which the load is always basically a noop anyway).
12101
12102 const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, capture == .by_ref);
12103
12104 if (sema.resolveSwitchCaptureFromProngItems(prong_items, capture == .by_ref)) |tag_val| {
12105 const payload_ref = try sema.resolveSwitchPayloadCaptureTaggedUnion(
12106 case_block,
12107 loaded_operand,
12108 operand_src,
12109 operand_ty,
12110 tag_val,
12111 capture == .by_ref,
12112 );
12113 const tag_ref: Air.Inst.Ref = if (has_tag_capture) .fromValue(tag_val) else .none;
12114 return .{ .payload_ref = payload_ref, .tag_ref = tag_ref };
12115 }
12116
12117 const payload_ref = try sema.analyzeSwitchPayloadCaptureTaggedUnion(
12118 case_block,
12119 operand,
12120 loaded_operand,
12121 operand_src,
12122 operand_ty,
12123 capture == .by_ref,
12124 capture_src,
12125 prong_items,
12126 );
12127
12128 const tag_ref: Air.Inst.Ref = tag_ref: {
12129 if (!has_tag_capture) break :tag_ref .none;
12130 const operand_val = switch (capture) {
12131 .none => unreachable, // handled above
12132 .by_val => loaded_operand,
12133 .by_ref => try sema.analyzeLoad(case_block, operand_src, loaded_operand, operand_src),
12134 };
12135 break :tag_ref try sema.unionToTag(case_block, operand_val);
12136 };
12137
12138 assert(!sema.typeOf(payload_ref).isNoReturn(zcu));
12139 return .{ .payload_ref = payload_ref, .tag_ref = tag_ref };
12140 }
12141
12142 const payload_ref: Air.Inst.Ref = payload_ref: {
12143 if (capture == .none) break :payload_ref .none;
12144
12145 if (operand_ty.zigTypeTag(zcu) == .error_set) {
12146 // Error captures need to have their type narrowed!
12147
12148 if (capture == .by_ref) {
12149 return sema.fail(
12150 case_block,
12151 capture_src,
12152 "error set cannot be captured by reference",
12153 .{},
12154 );
12155 }
12156 assert(capture == .by_val);
12157
12158 if (sema.resolveSwitchCaptureFromProngItems(prong_items, false)) |err_val| {
12159 const err_name = err_val.getErrorName(zcu).unwrap().?;
12160 break :payload_ref .fromIntern((try pt.intern(.{ .err = .{
12161 .ty = (try pt.singleErrorSetType(err_name)).toIntern(),
12162 .name = err_name,
12163 } })));
12164 }
12165
12166 const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, false);
12167
12168 switch (prong_items) {
12169 .@"inline" => unreachable, // handled above
12170 .has_ranges => unreachable, // not possible for error set
12171 .special => {
12172 const capture_err_ty = else_err_ty orelse {
12173 try sema.analyzeUnreachable(case_block, operand_src, false);
12174 break :payload_ref .unreachable_value;
12175 };
12176 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
12177 },
12178 .item_refs => |item_refs| {
12179 var names: InferredErrorSet.NameMap = .{};
12180 try names.ensureUnusedCapacity(sema.arena, item_refs.len);
12181 for (item_refs) |item_ref| {
12182 const item_val = sema.resolveValue(item_ref).?;
12183 names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {});
12184 }
12185 const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys());
12186 break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand);
12187 },
12188 }
12189 }
12190
12191 // We try to make the capture comptime-known based on `prong_items` first:
12192
12193 if (sema.resolveSwitchCaptureFromProngItems(prong_items, capture == .by_ref)) |item_val| {
12194 break :payload_ref switch (capture) {
12195 .none => unreachable, // handled above
12196 .by_val => .fromValue(item_val),
12197 .by_ref => try sema.uavRef(item_val),
12198 };
12199 }
12200
12201 // Otherwise the capture value is just the passed-through value of the
12202 // switch condition (which we might have to load first).
12203
12204 break :payload_ref try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, capture == .by_ref);
12205 };
12206
12207 if (has_tag_capture) {
12208 const tag_capture_src: LazySrcLoc = .{
12209 .base_node_inst = capture_src.base_node_inst,
12210 .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture },
12211 };
12212 return sema.failWithInvalidSwitchTagCapture(case_block, tag_capture_src, operand_ty);
12213 }
12214
12215 return .{ .payload_ref = payload_ref, .tag_ref = .none };
12216}
12217
12218fn resolveSwitchPayloadCaptureTaggedUnion(
12219 sema: *Sema,
12220 case_block: *Block,
12221 loaded_operand: Air.Inst.Ref,
12222 operand_src: LazySrcLoc,
12223 operand_ty: Type,
12224 tag_val: Value,
12225 capture_by_ref: bool,
12226) CompileError!Air.Inst.Ref {
12227 const pt = sema.pt;
12228 const zcu = pt.zcu;
12229 const ip = &zcu.intern_pool;
12230
12231 const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(tag_val, zcu).?);
12232 const union_obj = zcu.typeToUnion(operand_ty).?;
12233 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
12234 const payload_ref: Air.Inst.Ref = payload_ref: {
12235 if (capture_by_ref) {
12236 const ptr_field_ty = try sema.typeOf(loaded_operand).fieldPtrType(field_index, pt);
12237 if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |op_ptr_val| {
12238 if (op_ptr_val.isUndef(zcu)) break :payload_ref try pt.undefRef(ptr_field_ty);
12239 const field_ptr_val = try op_ptr_val.ptrField(field_index, pt);
12240 break :payload_ref .fromValue(try pt.getCoerced(field_ptr_val, ptr_field_ty));
12241 }
12242 break :payload_ref try case_block.addStructFieldPtr(loaded_operand, field_index, ptr_field_ty);
12243 }
12244 if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |union_val| {
12245 const tag_and_val = ip.indexToKey(union_val.toIntern()).un;
12246 break :payload_ref .fromIntern(tag_and_val.val);
12247 }
12248 if (try field_ty.onePossibleValue(pt)) |opv| break :payload_ref .fromValue(opv);
12249 break :payload_ref try case_block.addStructFieldVal(loaded_operand, field_index, field_ty);
12250 };
12251 assert(!sema.typeOf(payload_ref).isNoReturn(zcu));
12252 return payload_ref;
12253}
12254
12255fn analyzeSwitchPayloadCaptureTaggedUnion(
12256 sema: *Sema,
12257 case_block: *Block,
12258 operand: SwitchOperand,
12259 loaded_operand: Air.Inst.Ref,
12260 operand_src: LazySrcLoc,
12261 operand_ty: Type,
12262 capture_by_ref: bool,
12263 capture_src: LazySrcLoc,
12264 prong_items: SwitchProngItems,
12265) CompileError!Air.Inst.Ref {
12266 const pt = sema.pt;
12267 const zcu = pt.zcu;
12268 const ip = &zcu.intern_pool;
12269 const gpa = sema.gpa;
12270
12271 const item_refs: []const Air.Inst.Ref = switch (prong_items) {
12272 .@"inline" => unreachable, // handled above
12273 .has_ranges => unreachable, // not possible for tagged union
12274 .special => return loaded_operand,
12275 .item_refs => |item_refs| item_refs,
12276 };
12277
12278 const switch_node_offset = operand_src.offset.node_offset_switch_operand;
12279
12280 const union_obj = zcu.typeToUnion(operand_ty).?;
12281
12282 const first_item_val = sema.resolveValue(item_refs[0]).?;
12283 const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?;
12284 const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]);
12285
12286 const field_indices = try sema.arena.alloc(u32, item_refs.len);
12287 for (item_refs, field_indices) |item_ref, *field_idx| {
12288 const item_val = sema.resolveValue(item_ref).?;
12289 field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?;
12290 }
12291
12292 // Fast path: if all the operands are the same type already, we don't need to hit
12293 // PTR! This will also allow us to emit simpler code.
12294 const same_types = for (field_indices[1..]) |field_idx| {
12295 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12296 if (!field_ty.eql(first_field_ty)) break false;
12297 } else true;
12298
12299 const capture_ty: Type = capture_ty: {
12300 if (same_types) break :capture_ty first_field_ty;
12301 // We need values to run PTR on, so make a bunch of undef constants.
12302 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, item_refs.len);
12303 for (dummy_captures, field_indices) |*dummy, field_idx| {
12304 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]);
12305 dummy.* = try pt.undefRef(field_ty);
12306 }
12307
12308 const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len);
12309 for (item_srcs, 0..) |*item_src, item_i| {
12310 item_src.* = .{
12311 .base_node_inst = capture_src.base_node_inst,
12312 .offset = .{ .switch_case_item = .{
12313 .switch_node_offset = switch_node_offset,
12314 .case_idx = capture_src.offset.switch_capture.case_idx,
12315 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
12316 } },
12317 };
12318 }
12319
12320 break :capture_ty sema.resolvePeerTypes(
12321 case_block,
12322 capture_src,
12323 dummy_captures,
12324 .{ .override = item_srcs },
12325 ) catch |err| switch (err) {
12326 error.AlreadyReported => |e| {
12327 if (sema.err) |msg| {
12328 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
12329 }
12330 return e;
12331 },
12332 else => |e| return e,
12333 };
12334 };
12335
12336 // By-reference captures have some further restrictions which make them easier to emit
12337 if (capture_by_ref) {
12338 const operand_ptr_ty = sema.typeOf(loaded_operand);
12339 const capture_ptr_ty = resolve: {
12340 // By-ref captures of hetereogeneous types are only allowed if all field
12341 // pointer types are peer resolvable to each other.
12342 // We need values to run PTR on, so make a bunch of undef constants.
12343 const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, item_refs.len);
12344 for (field_indices, dummy_captures) |field_index, *dummy| {
12345 const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt);
12346 dummy.* = try pt.undefRef(field_ptr_ty);
12347 }
12348 const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len);
12349 for (item_srcs, 0..) |*item_src, item_i| {
12350 item_src.* = .{
12351 .base_node_inst = capture_src.base_node_inst,
12352 .offset = .{ .switch_case_item = .{
12353 .switch_node_offset = switch_node_offset,
12354 .case_idx = capture_src.offset.switch_capture.case_idx,
12355 .item_idx = .{ .kind = .single, .value = @intCast(item_i) },
12356 } },
12357 };
12358 }
12359
12360 break :resolve sema.resolvePeerTypes(
12361 case_block,
12362 capture_src,
12363 dummy_captures,
12364 .{ .override = item_srcs },
12365 ) catch |err| switch (err) {
12366 error.AlreadyReported => |e| {
12367 if (sema.err) |msg| {
12368 try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{});
12369 try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{});
12370 }
12371 return e;
12372 },
12373 else => |e| return e,
12374 };
12375 };
12376
12377 if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |op_ptr_val| {
12378 if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty);
12379 const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt);
12380 return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty));
12381 }
12382
12383 try sema.requireRuntimeBlock(case_block, operand_src, null);
12384 return case_block.addStructFieldPtr(loaded_operand, first_field_index, capture_ptr_ty);
12385 }
12386
12387 if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
12388
12389 if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |operand_val| {
12390 if (operand_val.isUndef(zcu)) return pt.undefRef(capture_ty);
12391 const union_val = ip.indexToKey(operand_val.toIntern()).un;
12392 if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty);
12393 const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val);
12394 return sema.coerce(case_block, capture_ty, uncoerced, operand_src);
12395 }
12396
12397 try sema.requireRuntimeBlock(case_block, operand_src, null);
12398
12399 if (same_types) {
12400 return case_block.addStructFieldVal(loaded_operand, first_field_index, capture_ty);
12401 }
12402
12403 // By-val capture with heterogeneous types which are not all in-memory coercible to
12404 // the resolved capture type. We finally have to fall back to the ugly method.
12405
12406 const capture_block_inst = try case_block.addInstAsIndex(.{
12407 .tag = .block,
12408 .data = .{
12409 .ty_pl = .{
12410 .ty = capture_ty,
12411 .payload = undefined, // updated below
12412 },
12413 },
12414 });
12415
12416 const estimated_extra = field_indices.len * 6 + (field_indices.len / 10); // 2 for Case, 1 item, probably 3 insts; plus hints
12417 var cases_extra = try std.ArrayList(u32).initCapacity(gpa, estimated_extra);
12418 defer cases_extra.deinit(gpa);
12419
12420 {
12421 // All branch hints are `.none`, so just add zero elems.
12422 comptime assert(@backingInt(std.lang.BranchHint.none) == 0);
12423 const need_elems = @divCeil(field_indices.len + 1, 10);
12424 try cases_extra.appendNTimes(gpa, 0, need_elems);
12425 }
12426
12427 {
12428 for (field_indices, item_refs, 0..) |field_index, item, item_index| {
12429 var coerce_block = case_block.makeSubBlock();
12430 defer coerce_block.instructions.deinit(sema.gpa);
12431
12432 const case_src: LazySrcLoc = .{
12433 .base_node_inst = capture_src.base_node_inst,
12434 .offset = .{ .switch_case_item = .{
12435 .switch_node_offset = switch_node_offset,
12436 .case_idx = capture_src.offset.switch_capture.case_idx,
12437 .item_idx = .{ .kind = .single, .value = @intCast(item_index) },
12438 } },
12439 };
12440
12441 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
12442 const uncoerced = try coerce_block.addStructFieldVal(loaded_operand, field_index, field_ty);
12443 const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src);
12444 _ = try coerce_block.addBr(capture_block_inst, coerced);
12445
12446 try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len +
12447 1 + // `item`, no ranges
12448 coerce_block.instructions.items.len);
12449 cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{
12450 .items_len = 1,
12451 .ranges_len = 0,
12452 .body_len = @intCast(coerce_block.instructions.items.len),
12453 }));
12454 cases_extra.appendAssumeCapacity(@backingInt(item)); // item
12455 cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body
12456 }
12457 }
12458 const else_body_len = len: {
12459 // 'else' prong is unreachable
12460 const result_index: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
12461 try sema.air_instructions.append(gpa, .{ .tag = .unreach, .data = .{ .no_op = {} } });
12462 try cases_extra.append(gpa, @backingInt(result_index));
12463 break :len 1;
12464 };
12465
12466 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len +
12467 cases_extra.items.len +
12468 @typeInfo(Air.Block).@"struct".field_names.len +
12469 1);
12470
12471 const switch_br_inst: u32 = @intCast(sema.air_instructions.len);
12472 try sema.air_instructions.append(gpa, .{
12473 .tag = .switch_br,
12474 .data = .{
12475 .pl_op = .{
12476 .operand = undefined, // set by switch below
12477 .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{
12478 .cases_len = @intCast(field_indices.len),
12479 .else_body_len = @intCast(else_body_len),
12480 }),
12481 },
12482 },
12483 });
12484 sema.air_extra.appendSliceAssumeCapacity(cases_extra.items);
12485
12486 // Set up block body
12487 switch (operand) {
12488 .simple => |s| {
12489 const air_datas = sema.air_instructions.items(.data);
12490 air_datas[switch_br_inst].pl_op.operand = s.cond;
12491 air_datas[@backingInt(capture_block_inst)].ty_pl.payload =
12492 sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 });
12493 sema.air_extra.appendAssumeCapacity(switch_br_inst);
12494 },
12495 .loop => {
12496 // The block must first extract the tag from the loaded union.
12497 const tag_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
12498 try sema.air_instructions.append(sema.gpa, .{
12499 .tag = .get_union_tag,
12500 .data = .{ .ty_op = .{
12501 .ty = .fromInterned(union_obj.enum_tag_type),
12502 .operand = loaded_operand,
12503 } },
12504 });
12505 const air_datas = sema.air_instructions.items(.data);
12506 air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef();
12507 air_datas[@backingInt(capture_block_inst)].ty_pl.payload =
12508 sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 });
12509 sema.air_extra.appendAssumeCapacity(@backingInt(tag_inst));
12510 sema.air_extra.appendAssumeCapacity(switch_br_inst);
12511 },
12512 }
12513
12514 return capture_block_inst.toRef();
12515}
12516
12517const ResolvedSwitchItem = struct {
12518 ref: Air.Inst.Ref,
12519 val: Value,
12520};
12521const ResolvedSwitchItemAndExtraIndex = struct { ResolvedSwitchItem, usize };
12522
12523fn resolveSwitchItem(
12524 sema: *Sema,
12525 block: *Block,
12526 item_src: LazySrcLoc,
12527 item_ty: Type,
12528 item_info: Zir.Inst.SwitchBlock.ItemInfo,
12529 extra_index: usize,
12530 switch_inst: Zir.Inst.Index,
12531 prong_is_comptime_unreach: bool,
12532) CompileError!ResolvedSwitchItemAndExtraIndex {
12533 const pt = sema.pt;
12534 const zcu = pt.zcu;
12535 const ip = &zcu.intern_pool;
12536 const comp = zcu.comp;
12537 const gpa = comp.gpa;
12538 const io = comp.io;
12539
12540 var end = extra_index;
12541 const uncoerced: Air.Inst.Ref, const uncoerced_ty: Type = uncoerced: switch (item_info.unwrap()) {
12542 .under => unreachable, // caller must check this before calling us
12543 .enum_literal => |str_index| {
12544 const zir_str = sema.code.nullTerminatedString(str_index);
12545 const name = try ip.getOrPutString(gpa, io, pt.tid, zir_str, .no_embedded_nulls);
12546 const uncoerced = try sema.analyzeDeclLiteral(block, item_src, name, item_ty, false);
12547 break :uncoerced .{ uncoerced, .enum_literal };
12548 },
12549 .error_value => |str_index| {
12550 const zir_str = sema.code.nullTerminatedString(str_index);
12551 const name = try ip.getOrPutString(gpa, io, pt.tid, zir_str, .no_embedded_nulls);
12552 // Make sure there's an error integer value associated with `name`.
12553 _ = try pt.getErrorValue(name);
12554 const err_set_ty = try pt.singleErrorSetType(name);
12555 const uncoerced = Air.internedToRef(try pt.intern(.{ .err = .{
12556 .ty = err_set_ty.toIntern(),
12557 .name = name,
12558 } }));
12559 break :uncoerced .{ uncoerced, err_set_ty };
12560 },
12561 .body_len => |body_len| {
12562 const body = sema.code.bodySlice(extra_index, body_len);
12563 end += body.len;
12564
12565 const uncoerced = ref: {
12566 // The result location of item bodies is `.{ .coerce_ty = switch_inst }`.
12567 sema.inst_map.putAssumeCapacity(switch_inst, .fromType(item_ty));
12568 defer assert(sema.inst_map.remove(switch_inst));
12569 const old_comptime_reason = block.comptime_reason;
12570 defer block.comptime_reason = old_comptime_reason;
12571 block.comptime_reason = .{ .reason = .{
12572 .src = item_src,
12573 .r = .{ .simple = .switch_item },
12574 } };
12575 break :ref try sema.resolveInlineBody(block, body, switch_inst);
12576 };
12577 break :uncoerced .{ uncoerced, sema.typeOf(uncoerced) };
12578 },
12579 };
12580 const item_ref: Air.Inst.Ref = item_ref: {
12581 if (item_ty.zigTypeTag(zcu) == .error_set and
12582 uncoerced_ty.zigTypeTag(zcu) == .error_set)
12583 {
12584 // We allow prongs with errors which are not part of the error set
12585 // being switched on if their prong body is `=> comptime unreachable,`.
12586 switch (try sema.coerceInMemoryAllowedErrorSets(block, item_ty, uncoerced_ty, item_src, item_src)) {
12587 .ok => if (sema.resolveValue(uncoerced)) |uncoerced_val| {
12588 break :item_ref .fromValue(try pt.getCoerced(uncoerced_val, item_ty));
12589 },
12590 .missing_error => if (prong_is_comptime_unreach) {
12591 break :item_ref uncoerced;
12592 },
12593 .from_anyerror => {},
12594 else => unreachable,
12595 }
12596 }
12597 break :item_ref try sema.coerce(block, item_ty, uncoerced, item_src);
12598 };
12599 const val = try sema.resolveConstDefinedValue(block, item_src, item_ref, .{ .simple = .switch_item });
12600 return .{ .{ .ref = item_ref, .val = val }, end };
12601}
12602
12603fn validateSwitchItemOrRange(
12604 sema: *Sema,
12605 block: *Block,
12606 item_src: LazySrcLoc,
12607 /// If `opt_last_val` is not `null`, this refers to the first val of a range.
12608 item_val: Value,
12609 opt_last_val: ?Value,
12610 item_ty: Type,
12611 seen: *ValidatedSwitchBlock.Seen,
12612) CompileError!void {
12613 const pt = sema.pt;
12614 const zcu = pt.zcu;
12615 const ip = &zcu.intern_pool;
12616 const maybe_prev_src: ?LazySrcLoc = maybe_prev_src: switch (item_ty.zigTypeTag(zcu)) {
12617 .@"enum" => {
12618 const int = ip.indexToKey(item_val.toIntern()).enum_tag.int;
12619 if (ip.loadEnumType(item_ty.toIntern()).tagValueIndex(ip, int)) |field_index| {
12620 const maybe_prev_src = seen.enum_fields[field_index];
12621 seen.enum_fields[field_index] = item_src;
12622 break :maybe_prev_src maybe_prev_src;
12623 } else {
12624 try seen.ranges.ensureUnusedCapacity(sema.arena, 1);
12625 break :maybe_prev_src if (seen.ranges.addAssumeCapacity(.{
12626 .first = .fromInterned(int),
12627 .last = .fromInterned(int),
12628 .src = item_src,
12629 }, .fromInterned(ip.typeOf(int)), zcu)) |prev| prev.src else null;
12630 }
12631 },
12632 .error_set => {
12633 const error_name = ip.indexToKey(item_val.toIntern()).err.name;
12634 break :maybe_prev_src if (seen.errors.fetchPutAssumeCapacity(error_name, item_src)) |prev|
12635 prev.value
12636 else
12637 null;
12638 },
12639 .int, .comptime_int => {
12640 const first_val = item_val;
12641 const last_val: Value = last_val: {
12642 const last_val = opt_last_val orelse break :last_val item_val;
12643 if (try first_val.compareAll(.gt, last_val, item_ty, pt)) {
12644 return sema.fail(block, item_src, "range start value is greater than the end value", .{});
12645 }
12646 break :last_val last_val;
12647 };
12648 if (seen.ranges.addAssumeCapacity(.{
12649 .first = first_val,
12650 .last = last_val,
12651 .src = item_src,
12652 }, item_ty, zcu)) |prev_range| {
12653 const overlap_start = first_val.numberMax(prev_range.first, zcu);
12654 const overlap_end = last_val.numberMin(prev_range.last, zcu);
12655 if (overlap_start.eql(overlap_end, item_ty, zcu)) {
12656 return sema.failWithOwnedErrorMsg(block, msg: {
12657 const msg = try sema.errMsg(item_src, "duplicate switch value '{f}'", .{
12658 overlap_start.fmtValueSema(pt, sema),
12659 });
12660 errdefer msg.destroy(sema.gpa);
12661 if (prev_range.first.eql(prev_range.last, item_ty, zcu)) {
12662 try sema.errNote(prev_range.src, msg, "previous value here", .{});
12663 } else {
12664 try sema.errNote(prev_range.src, msg, "previous value inside range here", .{});
12665 }
12666 break :msg msg;
12667 });
12668 }
12669 assert(!prev_range.first.eql(prev_range.last, item_ty, zcu));
12670 return sema.failWithOwnedErrorMsg(block, msg: {
12671 const msg = try sema.errMsg(item_src, "duplicate switch ranges", .{});
12672 errdefer msg.destroy(sema.gpa);
12673 if (first_val.eql(prev_range.first, item_ty, zcu) and
12674 last_val.eql(prev_range.last, item_ty, zcu))
12675 {
12676 try sema.errNote(prev_range.src, msg, "previous range here", .{});
12677 } else {
12678 try sema.errNote(prev_range.src, msg, "overlaps with previous range here", .{});
12679 try sema.errNote(prev_range.src, msg, "ranges overlap from '{f}' to '{f}'", .{
12680 overlap_start.fmtValueSema(pt, sema), overlap_end.fmtValueSema(pt, sema),
12681 });
12682 }
12683 break :msg msg;
12684 });
12685 }
12686 break :maybe_prev_src null;
12687 },
12688 .@"union", .@"struct" => {
12689 const backing_int_val = ip.indexToKey(item_val.toIntern()).bitpack.backing_int_val;
12690 break :maybe_prev_src if (seen.ranges.addAssumeCapacity(.{
12691 .first = .fromInterned(backing_int_val),
12692 .last = .fromInterned(backing_int_val),
12693 .src = item_src,
12694 }, item_ty.backingIntType(zcu), zcu)) |prev| prev.src else null;
12695 },
12696 .enum_literal, .@"fn", .pointer, .type => {
12697 break :maybe_prev_src if (seen.sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev|
12698 prev.value
12699 else
12700 null;
12701 },
12702 .bool => {
12703 if (item_val.toBool()) {
12704 if (seen.true_src) |prev_src| break :maybe_prev_src prev_src;
12705 seen.true_src = item_src;
12706 } else {
12707 if (seen.false_src) |prev_src| break :maybe_prev_src prev_src;
12708 seen.false_src = item_src;
12709 }
12710 break :maybe_prev_src null;
12711 },
12712 .void => {
12713 if (seen.void_src) |prev_src| break :maybe_prev_src prev_src;
12714 seen.void_src = item_src;
12715 break :maybe_prev_src null;
12716 },
12717 else => unreachable, // should have already checked for invalid types
12718 };
12719 if (maybe_prev_src) |prev_src| {
12720 return sema.failWithOwnedErrorMsg(block, msg: {
12721 const msg = try sema.errMsg(item_src, "duplicate switch value '{f}'", .{
12722 item_val.fmtValueSema(pt, sema),
12723 });
12724 errdefer msg.destroy(sema.gpa);
12725 try sema.errNote(prev_src, msg, "previous value here", .{});
12726 if (item_ty.zigTypeTag(zcu) == .type) {
12727 try sema.addDeclaredHereNote(msg, item_val.toType());
12728 } else {
12729 try sema.addDeclaredHereNote(msg, item_ty);
12730 }
12731 break :msg msg;
12732 });
12733 }
12734}
12735
12736fn maybeErrorUnwrap(
12737 sema: *Sema,
12738 block: *Block,
12739 body: []const Zir.Inst.Index,
12740 operand: Air.Inst.Ref,
12741 operand_src: LazySrcLoc,
12742 allow_err_code_inst: bool,
12743) !bool {
12744 const pt = sema.pt;
12745 const zcu = pt.zcu;
12746
12747 const tags = sema.code.instructions.items(.tag);
12748 for (body) |inst| {
12749 switch (tags[@backingInt(inst)]) {
12750 .@"unreachable" => if (!block.wantSafety()) return false,
12751 .err_union_code => if (!allow_err_code_inst) return false,
12752 .save_err_ret_index,
12753 .dbg_stmt,
12754 .str,
12755 .as_node,
12756 .panic,
12757 => {},
12758 else => return false,
12759 }
12760 }
12761
12762 for (body) |inst| {
12763 const air_inst = switch (tags[@backingInt(inst)]) {
12764 .err_union_code => continue,
12765 .dbg_stmt => {
12766 try sema.zirDbgStmt(block, inst);
12767 continue;
12768 },
12769 .save_err_ret_index => {
12770 try sema.zirSaveErrRetIndex(block, inst);
12771 continue;
12772 },
12773 .str => try sema.zirStr(inst),
12774 .as_node => try sema.zirAsNode(block, inst),
12775 .@"unreachable" => {
12776 try safetyPanicUnwrapError(sema, block, operand_src, operand);
12777 return true;
12778 },
12779 .panic => {
12780 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
12781 const msg_inst = sema.resolveInst(inst_data.operand);
12782
12783 const panic_fn = try getStdLangValue(sema, operand_src, .@"panic.call");
12784 const args: [2]Air.Inst.Ref = .{ msg_inst, .null_value };
12785 try sema.callBuiltin(block, operand_src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check");
12786 return true;
12787 },
12788 else => unreachable,
12789 };
12790 if (sema.typeOf(air_inst).isNoReturn(zcu))
12791 return true;
12792 sema.inst_map.putAssumeCapacity(inst, air_inst);
12793 }
12794 unreachable;
12795}
12796
12797fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void {
12798 const pt = sema.pt;
12799 const zcu = pt.zcu;
12800 const index = cond.toIndex() orelse return;
12801 if (sema.code.instructions.items(.tag)[@backingInt(index)] != .is_non_err) return;
12802
12803 const err_inst_data = sema.code.instructions.items(.data)[@backingInt(index)].un_node;
12804 const err_operand = sema.resolveInst(err_inst_data.operand);
12805 const operand_ty = sema.typeOf(err_operand);
12806 if (operand_ty.zigTypeTag(zcu) == .error_set) {
12807 try sema.maybeErrorUnwrapComptime(block, body, err_operand);
12808 return;
12809 }
12810 if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| {
12811 if (!operand_ty.isError(zcu)) return;
12812 if (val.getErrorName(zcu) == .none) return;
12813 try sema.maybeErrorUnwrapComptime(block, body, err_operand);
12814 }
12815}
12816
12817fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void {
12818 const tags = sema.code.instructions.items(.tag);
12819 const inst = for (body) |inst| {
12820 switch (tags[@backingInt(inst)]) {
12821 .dbg_stmt,
12822 .save_err_ret_index,
12823 => {},
12824 .@"unreachable" => break inst,
12825 else => return,
12826 }
12827 } else return;
12828 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"unreachable";
12829 const src = block.nodeOffset(inst_data.src_node);
12830
12831 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
12832 if (val.getErrorName(sema.pt.zcu).unwrap()) |name| {
12833 return sema.failWithComptimeErrorRetTrace(block, src, name);
12834 }
12835 }
12836}
12837
12838fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
12839 const pt = sema.pt;
12840 const zcu = pt.zcu;
12841 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
12842 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
12843 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
12844 const name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
12845 const ty = try sema.resolveType(block, ty_src, extra.lhs);
12846 const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name });
12847 try sema.ensureLayoutResolved(ty, ty_src, .field_queried);
12848 const ip = &zcu.intern_pool;
12849
12850 const has_field = hf: {
12851 switch (ip.indexToKey(ty.toIntern())) {
12852 .ptr_type => |ptr_type| switch (ptr_type.flags.size) {
12853 .slice => {
12854 if (field_name.eqlSlice("ptr", ip)) break :hf true;
12855 if (field_name.eqlSlice("len", ip)) break :hf true;
12856 break :hf false;
12857 },
12858 else => {},
12859 },
12860 .tuple_type => |tuple| {
12861 const field_index = field_name.toUnsigned(ip) orelse break :hf false;
12862 break :hf field_index < tuple.types.len;
12863 },
12864 .struct_type => {
12865 break :hf ip.loadStructType(ty.toIntern()).nameIndex(ip, field_name) != null;
12866 },
12867 .union_type => {
12868 const union_type = ip.loadUnionType(ty.toIntern());
12869 const enum_type = ip.loadEnumType(union_type.enum_tag_type);
12870 break :hf enum_type.nameIndex(ip, field_name) != null;
12871 },
12872 .enum_type => {
12873 break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null;
12874 },
12875 .array_type => break :hf field_name.eqlSlice("len", ip),
12876 else => {},
12877 }
12878 return sema.fail(block, ty_src, "type '{f}' does not support '@hasField'", .{
12879 ty.fmt(pt),
12880 });
12881 };
12882 return if (has_field) .bool_true else .bool_false;
12883}
12884
12885fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
12886 const pt = sema.pt;
12887 const zcu = pt.zcu;
12888 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
12889 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
12890 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
12891 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
12892 const container_type = try sema.resolveType(block, lhs_src, extra.lhs);
12893 const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{ .simple = .decl_name });
12894
12895 try sema.checkNamespaceType(block, lhs_src, container_type);
12896
12897 const namespace = container_type.getNamespace(zcu).unwrap() orelse return .bool_false;
12898 if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| {
12899 if (lookup.accessible == .public) {
12900 return .bool_true;
12901 }
12902 }
12903 return .bool_false;
12904}
12905
12906fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
12907 const pt = sema.pt;
12908 const zcu = pt.zcu;
12909 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_tok;
12910 const extra = sema.code.extraData(Zir.Inst.Import, inst_data.payload_index).data;
12911 const operand_src = block.tokenOffset(inst_data.src_tok);
12912 const operand = sema.code.nullTerminatedString(extra.path);
12913
12914 const result = pt.doImport(block.getFileScope(zcu), operand) catch |err| switch (err) {
12915 error.ModuleNotFound => return sema.fail(block, operand_src, "no module named '{s}' available within module '{s}'", .{
12916 operand, block.getFileScope(zcu).mod.?.fully_qualified_name,
12917 }),
12918 error.IllegalZigImport => unreachable, // caught before semantic analysis
12919 error.OutOfMemory => |e| return e,
12920 };
12921 const file_index = result.file;
12922 const file = zcu.fileByIndex(file_index);
12923 try sema.declareDependency(.{ .source_file = file_index });
12924 switch (file.getMode()) {
12925 .zig => {
12926 try pt.ensureFilePopulated(file_index);
12927 const ty: Type = .fromInterned(zcu.fileRootType(file_index));
12928 try sema.addTypeReferenceEntry(operand_src, ty);
12929 // No need for `ensureNamespaceUpToDate`, because `Zcu.PerThread.updateFileNamespace`
12930 // already made sure that all root file structs have up-to-date namespaces.
12931 return .fromType(ty);
12932 },
12933 .zon => {
12934 const res_ty: InternPool.Index = b: {
12935 if (extra.res_ty == .none) break :b .none;
12936 const res_ty_inst = sema.resolveInst(extra.res_ty);
12937 const res_ty = try sema.analyzeAsType(block, operand_src, .type, res_ty_inst);
12938 if (res_ty.isGenericPoison()) break :b .none;
12939 break :b res_ty.toIntern();
12940 };
12941 const interned = try LowerZon.run(
12942 sema,
12943 file,
12944 file_index,
12945 res_ty,
12946 operand_src,
12947 block,
12948 );
12949 return Air.internedToRef(interned);
12950 },
12951 }
12952}
12953
12954fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
12955 const pt = sema.pt;
12956 const zcu = pt.zcu;
12957
12958 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
12959 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
12960 const name = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .operand_embedFile });
12961
12962 if (name.len == 0) {
12963 return sema.fail(block, operand_src, "file path name cannot be empty", .{});
12964 }
12965
12966 const ef_idx = pt.embedFile(block.getFileScope(zcu), name) catch |err| switch (err) {
12967 error.ImportOutsideModulePath => {
12968 return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name});
12969 },
12970 error.OutOfMemory => |e| return e,
12971 error.Canceled => |e| return e,
12972 };
12973 try sema.declareDependency(.{ .embed_file = ef_idx });
12974
12975 const result = ef_idx.get(zcu);
12976 if (result.val == .none) {
12977 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(result.err.?) });
12978 }
12979
12980 return Air.internedToRef(result.val);
12981}
12982
12983fn zirShl(
12984 sema: *Sema,
12985 block: *Block,
12986 inst: Zir.Inst.Index,
12987 air_tag: Air.Inst.Tag,
12988) CompileError!Air.Inst.Ref {
12989 const pt = sema.pt;
12990 const zcu = pt.zcu;
12991 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
12992 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
12993 const lhs = sema.resolveInst(extra.lhs);
12994 const rhs = sema.resolveInst(extra.rhs);
12995 const lhs_ty = sema.typeOf(lhs);
12996 const rhs_ty = sema.typeOf(rhs);
12997
12998 const src = block.nodeOffset(inst_data.src_node);
12999 const lhs_src, const rhs_src = switch (air_tag) {
13000 .shl, .shl_sat => .{
13001 block.src(.{ .node_offset_bin_lhs = inst_data.src_node }),
13002 block.src(.{ .node_offset_bin_rhs = inst_data.src_node }),
13003 },
13004 .shl_exact => .{
13005 block.builtinCallArgSrc(inst_data.src_node, 0),
13006 block.builtinCallArgSrc(inst_data.src_node, 1),
13007 },
13008 else => unreachable,
13009 };
13010
13011 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
13012
13013 const scalar_ty = lhs_ty.scalarType(zcu);
13014 const scalar_rhs_ty = rhs_ty.scalarType(zcu);
13015
13016 // AstGen currently forces the rhs of `<<` to coerce to the correct type before the `.shl` instruction, so
13017 // we already know `scalar_rhs_ty` is valid for `.shl`; likewise the lhs is validated when its
13018 // `typeof_log2_int_type` is evaluated. `.shl_sat` gets neither coercion, so validate both operands here.
13019 if (air_tag == .shl_sat) {
13020 _ = try sema.log2IntType(block, lhs_ty, lhs_src);
13021 _ = try sema.checkIntType(block, rhs_src, scalar_rhs_ty);
13022 }
13023
13024 const maybe_lhs_val = sema.resolveValue(lhs);
13025 const maybe_rhs_val = sema.resolveValue(rhs);
13026
13027 const runtime_src = rs: {
13028 if (maybe_rhs_val) |rhs_val| {
13029 if (maybe_lhs_val) |lhs_val| {
13030 return .fromValue(try arith.shl(sema, block, lhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, switch (air_tag) {
13031 .shl => .shl,
13032 .shl_sat => .shl_sat,
13033 .shl_exact => .shl_exact,
13034 else => unreachable,
13035 }));
13036 }
13037 if (rhs_val.isUndef(zcu)) switch (air_tag) {
13038 .shl_sat => return pt.undefRef(lhs_ty),
13039 .shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, null),
13040 else => unreachable,
13041 };
13042 const bits = scalar_ty.intInfo(zcu).bits;
13043 switch (rhs_ty.zigTypeTag(zcu)) {
13044 .int, .comptime_int => {
13045 switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
13046 .gt => {
13047 if (air_tag != .shl_sat) {
13048 var rhs_space: Value.BigIntSpace = undefined;
13049 const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu);
13050 if (rhs_bigint.orderAgainstScalar(bits) != .lt) {
13051 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null);
13052 }
13053 }
13054 },
13055 .eq => return lhs,
13056 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null),
13057 }
13058 },
13059 .vector => {
13060 var any_positive: bool = false;
13061 for (0..rhs_ty.vectorLen(zcu)) |elem_idx| {
13062 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
13063 if (rhs_elem.isUndef(zcu)) switch (air_tag) {
13064 .shl_sat => continue,
13065 .shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, elem_idx),
13066 else => unreachable,
13067 };
13068 switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) {
13069 .gt => {
13070 if (air_tag != .shl_sat) {
13071 var rhs_elem_space: Value.BigIntSpace = undefined;
13072 const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu);
13073 if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) {
13074 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx);
13075 }
13076 }
13077 any_positive = true;
13078 },
13079 .eq => {},
13080 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx),
13081 }
13082 }
13083 if (!any_positive) return lhs;
13084 },
13085 else => unreachable,
13086 }
13087 break :rs lhs_src;
13088 } else {
13089 if (air_tag == .shl_sat and scalar_rhs_ty.isSignedInt(zcu)) {
13090 return sema.fail(block, rhs_src, "shift by signed type '{f}'", .{rhs_ty.fmt(pt)});
13091 }
13092 if (scalar_ty.toIntern() == .comptime_int_type) {
13093 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
13094 }
13095 if (maybe_lhs_val) |lhs_val| {
13096 switch (air_tag) {
13097 .shl_sat => if (lhs_val.isUndef(zcu)) return pt.undefRef(lhs_ty),
13098 .shl, .shl_exact => try sema.checkAllScalarsDefined(block, lhs_src, lhs_val),
13099 else => unreachable,
13100 }
13101 if (lhs_val.compareAllWithZero(.eq, zcu)) return lhs;
13102 }
13103 }
13104 break :rs rhs_src;
13105 };
13106 const rt_rhs: Air.Inst.Ref = switch (air_tag) {
13107 else => unreachable,
13108 .shl, .shl_exact => rhs,
13109 // The backend can handle a large runtime rhs better than we can, but
13110 // we can limit a large comptime rhs better here. This also has the
13111 // necessary side effect of preventing rhs from being a `comptime_int`.
13112 .shl_sat => if (maybe_rhs_val) |rhs_val| .fromValue(rt_rhs: {
13113 const bit_count = scalar_ty.intInfo(zcu).bits;
13114 const rt_rhs_scalar_ty = try pt.smallestUnsignedInt(bit_count);
13115 if (!rhs_ty.isVector(zcu)) break :rt_rhs try pt.intValue(
13116 rt_rhs_scalar_ty,
13117 @min(rhs_val.getUnsignedInt(zcu) orelse bit_count, bit_count),
13118 );
13119 const rhs_len = rhs_ty.vectorLen(zcu);
13120 const rhs_elems = try sema.arena.alloc(InternPool.Index, rhs_len);
13121 for (rhs_elems, 0..) |*rhs_elem, i| rhs_elem.* = (try pt.intValue(
13122 rt_rhs_scalar_ty,
13123 @min((try rhs_val.elemValue(pt, i)).getUnsignedInt(zcu) orelse bit_count, bit_count),
13124 )).toIntern();
13125 break :rt_rhs try pt.aggregateValue(try pt.vectorType(.{
13126 .len = rhs_len,
13127 .child = rt_rhs_scalar_ty.toIntern(),
13128 }), rhs_elems);
13129 }) else rhs,
13130 };
13131
13132 try sema.requireRuntimeBlock(block, src, runtime_src);
13133 if (block.wantSafety()) {
13134 const bit_count = scalar_ty.intInfo(zcu).bits;
13135 if (air_tag != .shl_sat and !std.math.isPowerOfTwo(bit_count)) {
13136 const bit_count_val = try pt.intValue(scalar_rhs_ty, bit_count);
13137 const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
13138 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern());
13139 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
13140 break :ok try block.addReduce(lt, .And);
13141 } else ok: {
13142 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern());
13143 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
13144 };
13145 try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
13146 }
13147
13148 if (air_tag == .shl_exact) {
13149 const op_ov_tuple_ty = try pt.overflowArithmeticTupleType(lhs_ty);
13150 const op_ov = try block.addInst(.{
13151 .tag = .shl_with_overflow,
13152 .data = .{ .ty_pl = .{
13153 .ty = op_ov_tuple_ty,
13154 .payload = try sema.addExtra(Air.Bin{
13155 .lhs = lhs,
13156 .rhs = rhs,
13157 }),
13158 } },
13159 });
13160 const ov_bit = try sema.tupleFieldValByIndex(block, op_ov, 1, op_ov_tuple_ty);
13161 const any_ov_bit = if (lhs_ty.zigTypeTag(zcu) == .vector)
13162 try block.addReduce(ov_bit, .Or)
13163 else
13164 ov_bit;
13165 const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, .zero_u1);
13166
13167 try sema.addSafetyCheck(block, src, no_ov, .shl_overflow);
13168 return sema.tupleFieldValByIndex(block, op_ov, 0, op_ov_tuple_ty);
13169 }
13170 }
13171 return block.addBinOp(air_tag, lhs, rt_rhs);
13172}
13173
13174fn zirShr(
13175 sema: *Sema,
13176 block: *Block,
13177 inst: Zir.Inst.Index,
13178 air_tag: Air.Inst.Tag,
13179) CompileError!Air.Inst.Ref {
13180 const pt = sema.pt;
13181 const zcu = pt.zcu;
13182 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
13183 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
13184 const lhs = sema.resolveInst(extra.lhs);
13185 const rhs = sema.resolveInst(extra.rhs);
13186 const lhs_ty = sema.typeOf(lhs);
13187 const rhs_ty = sema.typeOf(rhs);
13188
13189 const src = block.nodeOffset(inst_data.src_node);
13190 const lhs_src = switch (air_tag) {
13191 .shr => block.src(.{ .node_offset_bin_lhs = inst_data.src_node }),
13192 .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 0),
13193 else => unreachable,
13194 };
13195 const rhs_src = switch (air_tag) {
13196 .shr => block.src(.{ .node_offset_bin_rhs = inst_data.src_node }),
13197 .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 1),
13198 else => unreachable,
13199 };
13200
13201 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
13202 const scalar_ty = lhs_ty.scalarType(zcu);
13203
13204 const maybe_lhs_val = sema.resolveValue(lhs);
13205 const maybe_rhs_val = sema.resolveValue(rhs);
13206
13207 const runtime_src = rs: {
13208 if (maybe_rhs_val) |rhs_val| {
13209 if (maybe_lhs_val) |lhs_val| {
13210 return .fromValue(try arith.shr(sema, block, lhs_ty, rhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, switch (air_tag) {
13211 .shr => .shr,
13212 .shr_exact => .shr_exact,
13213 else => unreachable,
13214 }));
13215 }
13216 if (rhs_val.isUndef(zcu)) {
13217 return sema.failWithUseOfUndef(block, rhs_src, null);
13218 }
13219 const bits = scalar_ty.intInfo(zcu).bits;
13220 switch (rhs_ty.zigTypeTag(zcu)) {
13221 .int, .comptime_int => {
13222 switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
13223 .gt => {
13224 var rhs_space: Value.BigIntSpace = undefined;
13225 const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu);
13226 if (rhs_bigint.orderAgainstScalar(bits) != .lt) {
13227 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null);
13228 }
13229 },
13230 .eq => return lhs,
13231 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null),
13232 }
13233 },
13234 .vector => {
13235 var any_positive: bool = false;
13236 for (0..rhs_ty.vectorLen(zcu)) |elem_idx| {
13237 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
13238 if (rhs_elem.isUndef(zcu)) {
13239 return sema.failWithUseOfUndef(block, rhs_src, elem_idx);
13240 }
13241 switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) {
13242 .gt => {
13243 var rhs_elem_space: Value.BigIntSpace = undefined;
13244 const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu);
13245 if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) {
13246 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx);
13247 }
13248 any_positive = true;
13249 },
13250 .eq => {},
13251 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx),
13252 }
13253 }
13254 if (!any_positive) return lhs;
13255 },
13256 else => unreachable,
13257 }
13258 break :rs lhs_src;
13259 } else {
13260 if (scalar_ty.toIntern() == .comptime_int_type) {
13261 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
13262 }
13263 if (maybe_lhs_val) |lhs_val| {
13264 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
13265 if (lhs_val.compareAllWithZero(.eq, zcu)) return lhs;
13266 }
13267 }
13268 break :rs rhs_src;
13269 };
13270 try sema.requireRuntimeBlock(block, src, runtime_src);
13271 const result = try block.addBinOp(air_tag, lhs, rhs);
13272 if (block.wantSafety()) {
13273 const bit_count = scalar_ty.intInfo(zcu).bits;
13274 if (!std.math.isPowerOfTwo(bit_count)) {
13275 const bit_count_val = try pt.intValue(rhs_ty.scalarType(zcu), bit_count);
13276
13277 const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
13278 const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern());
13279 const lt = try block.addCmpVector(rhs, bit_count_inst, .lt);
13280 break :ok try block.addReduce(lt, .And);
13281 } else ok: {
13282 const bit_count_inst = Air.internedToRef(bit_count_val.toIntern());
13283 break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst);
13284 };
13285 try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big);
13286 }
13287
13288 if (air_tag == .shr_exact) {
13289 const back = try block.addBinOp(.shl, result, rhs);
13290
13291 const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: {
13292 const eql = try block.addCmpVector(lhs, back, .eq);
13293 break :ok try block.addReduce(eql, .And);
13294 } else try block.addBinOp(.cmp_eq, lhs, back);
13295 try sema.addSafetyCheck(block, src, ok, .shr_overflow);
13296 }
13297 }
13298 return result;
13299}
13300
13301fn zirBitwise(
13302 sema: *Sema,
13303 block: *Block,
13304 inst: Zir.Inst.Index,
13305 air_tag: Air.Inst.Tag,
13306) CompileError!Air.Inst.Ref {
13307 const pt = sema.pt;
13308 const zcu = pt.zcu;
13309 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
13310 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
13311 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
13312 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
13313 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
13314 const lhs = sema.resolveInst(extra.lhs);
13315 const rhs = sema.resolveInst(extra.rhs);
13316 const lhs_ty = sema.typeOf(lhs);
13317 const rhs_ty = sema.typeOf(rhs);
13318 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
13319
13320 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
13321 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
13322 const scalar_type = resolved_type.scalarType(zcu);
13323 const scalar_tag = scalar_type.zigTypeTag(zcu);
13324
13325 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
13326 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
13327
13328 const is_int_or_bool = scalar_tag == .int or scalar_tag == .comptime_int or scalar_tag == .bool;
13329
13330 if (!is_int_or_bool) {
13331 return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(zcu)), @tagName(rhs_ty.zigTypeTag(zcu)) });
13332 }
13333
13334 const runtime_src = runtime: {
13335 // TODO: ask the linker what kind of relocations are available, and
13336 // in some cases emit a Value that means "this decl's address AND'd with this operand".
13337 if (sema.resolveValue(casted_lhs)) |lhs_val| {
13338 if (sema.resolveValue(casted_rhs)) |rhs_val| {
13339 const result_val = switch (air_tag) {
13340 // zig fmt: off
13341 .bit_and => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"and"),
13342 .bit_or => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"or"),
13343 .xor => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .xor),
13344 else => unreachable,
13345 // zig fmt: on
13346 };
13347 return Air.internedToRef(result_val.toIntern());
13348 } else {
13349 break :runtime rhs_src;
13350 }
13351 } else {
13352 break :runtime lhs_src;
13353 }
13354 };
13355
13356 try sema.requireRuntimeBlock(block, src, runtime_src);
13357 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
13358}
13359
13360fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
13361 const pt = sema.pt;
13362 const zcu = pt.zcu;
13363 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
13364 const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
13365 const src = block.nodeOffset(inst_data.src_node);
13366 const operand = sema.resolveInst(inst_data.operand);
13367 const operand_ty = sema.typeOf(operand);
13368 const scalar_ty = operand_ty.scalarType(zcu);
13369 const scalar_tag = scalar_ty.zigTypeTag(zcu);
13370
13371 if (scalar_tag != .int and scalar_tag != .bool)
13372 return sema.fail(block, operand_src, "bitwise not operation on type '{f}'", .{operand_ty.fmt(pt)});
13373
13374 return analyzeBitNot(sema, block, operand, src);
13375}
13376
13377fn analyzeBitNot(
13378 sema: *Sema,
13379 block: *Block,
13380 operand: Air.Inst.Ref,
13381 src: LazySrcLoc,
13382) CompileError!Air.Inst.Ref {
13383 const operand_ty = sema.typeOf(operand);
13384 if (sema.resolveValue(operand)) |operand_val| {
13385 const result_val = try arith.bitwiseNot(sema, operand_ty, operand_val);
13386 return Air.internedToRef(result_val.toIntern());
13387 }
13388 try sema.requireRuntimeBlock(block, src, null);
13389 return block.addTyOp(.not, operand_ty, operand);
13390}
13391
13392fn analyzeTupleCat(
13393 sema: *Sema,
13394 block: *Block,
13395 src_node: std.zig.Ast.Node.Offset,
13396 lhs: Air.Inst.Ref,
13397 rhs: Air.Inst.Ref,
13398) CompileError!Air.Inst.Ref {
13399 const pt = sema.pt;
13400 const zcu = pt.zcu;
13401 const comp = zcu.comp;
13402 const gpa = comp.gpa;
13403 const io = comp.io;
13404
13405 const lhs_ty = sema.typeOf(lhs);
13406 const rhs_ty = sema.typeOf(rhs);
13407 const src = block.nodeOffset(src_node);
13408
13409 const lhs_len = lhs_ty.structFieldCount(zcu);
13410 const rhs_len = rhs_ty.structFieldCount(zcu);
13411 const dest_fields = lhs_len + rhs_len;
13412
13413 if (dest_fields == 0) {
13414 return .empty_tuple;
13415 }
13416 if (lhs_len == 0) {
13417 return rhs;
13418 }
13419 if (rhs_len == 0) {
13420 return lhs;
13421 }
13422 const final_len = try sema.usizeCast(block, src, dest_fields);
13423
13424 const types = try sema.arena.alloc(InternPool.Index, final_len);
13425 const values = try sema.arena.alloc(InternPool.Index, final_len);
13426
13427 const opt_runtime_src = rs: {
13428 var runtime_src: ?LazySrcLoc = null;
13429 var i: u32 = 0;
13430 while (i < lhs_len) : (i += 1) {
13431 types[i] = lhs_ty.fieldType(i, zcu).toIntern();
13432 const operand_src = block.src(.{ .array_cat_lhs = .{
13433 .array_cat_offset = src_node,
13434 .elem_index = i,
13435 } });
13436 if (lhs_ty.structFieldDefaultValue(i, zcu)) |default_val| {
13437 values[i] = default_val.toIntern();
13438 } else {
13439 runtime_src = operand_src;
13440 values[i] = .none;
13441 }
13442 }
13443 i = 0;
13444 while (i < rhs_len) : (i += 1) {
13445 types[i + lhs_len] = rhs_ty.fieldType(i, zcu).toIntern();
13446 const operand_src = block.src(.{ .array_cat_rhs = .{
13447 .array_cat_offset = src_node,
13448 .elem_index = i,
13449 } });
13450 if (rhs_ty.structFieldDefaultValue(i, zcu)) |default_val| {
13451 values[i + lhs_len] = default_val.toIntern();
13452 } else {
13453 runtime_src = operand_src;
13454 values[i + lhs_len] = .none;
13455 }
13456 }
13457 break :rs runtime_src;
13458 };
13459
13460 const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{
13461 .types = types,
13462 .values = values,
13463 }));
13464
13465 const runtime_src = opt_runtime_src orelse {
13466 const tuple_val = try pt.aggregateValue(tuple_ty, values);
13467 return Air.internedToRef(tuple_val.toIntern());
13468 };
13469
13470 try sema.requireRuntimeBlock(block, src, runtime_src);
13471
13472 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
13473 var i: u32 = 0;
13474 while (i < lhs_len) : (i += 1) {
13475 element_refs[i] = try sema.tupleFieldValByIndex(block, lhs, i, lhs_ty);
13476 }
13477 i = 0;
13478 while (i < rhs_len) : (i += 1) {
13479 element_refs[i + lhs_len] =
13480 try sema.tupleFieldValByIndex(block, rhs, i, rhs_ty);
13481 }
13482
13483 return block.addAggregateInit(tuple_ty, element_refs);
13484}
13485
13486fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
13487 const pt = sema.pt;
13488 const zcu = pt.zcu;
13489 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
13490 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
13491 const lhs = sema.resolveInst(extra.lhs);
13492 const rhs = sema.resolveInst(extra.rhs);
13493 const lhs_ty = sema.typeOf(lhs);
13494 const rhs_ty = sema.typeOf(rhs);
13495 const src = block.nodeOffset(inst_data.src_node);
13496
13497 const lhs_is_tuple = lhs_ty.isTuple(zcu);
13498 const rhs_is_tuple = rhs_ty.isTuple(zcu);
13499 if (lhs_is_tuple and rhs_is_tuple) {
13500 return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs);
13501 }
13502
13503 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
13504 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
13505
13506 const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: {
13507 if (lhs_is_tuple) break :lhs_info undefined;
13508 return sema.fail(block, lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)});
13509 };
13510 const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse {
13511 assert(!rhs_is_tuple);
13512 return sema.fail(block, rhs_src, "expected indexable; found '{f}'", .{rhs_ty.fmt(pt)});
13513 };
13514
13515 const resolved_elem_ty = t: {
13516 var trash_block = block.makeSubBlock();
13517 trash_block.comptime_reason = null;
13518 defer trash_block.instructions.deinit(sema.gpa);
13519
13520 const instructions = [_]Air.Inst.Ref{
13521 try trash_block.addTyOp(.bit_cast, lhs_info.elem_type, .void_value),
13522 try trash_block.addTyOp(.bit_cast, rhs_info.elem_type, .void_value),
13523 };
13524 break :t try sema.resolvePeerTypes(block, src, &instructions, .{
13525 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
13526 });
13527 };
13528
13529 // When there is a sentinel mismatch, no sentinel on the result.
13530 // Otherwise, use the sentinel value provided by either operand,
13531 // coercing it to the peer-resolved element type.
13532 const res_sent_val: ?Value = s: {
13533 if (lhs_info.sentinel) |lhs_sent_val| {
13534 const lhs_sent = Air.internedToRef(lhs_sent_val.toIntern());
13535 if (rhs_info.sentinel) |rhs_sent_val| {
13536 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern());
13537 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
13538 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
13539 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?;
13540 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?;
13541 if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) {
13542 break :s lhs_sent_casted_val;
13543 } else {
13544 break :s null;
13545 }
13546 } else {
13547 const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src);
13548 const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?;
13549 break :s lhs_sent_casted_val;
13550 }
13551 } else {
13552 if (rhs_info.sentinel) |rhs_sent_val| {
13553 const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern());
13554 const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src);
13555 const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?;
13556 break :s rhs_sent_casted_val;
13557 } else {
13558 break :s null;
13559 }
13560 }
13561 };
13562
13563 const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len);
13564 const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len);
13565 const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) {
13566 error.Overflow => return sema.fail(
13567 block,
13568 src,
13569 "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle",
13570 .{ lhs_len, rhs_len },
13571 ),
13572 };
13573
13574 const result_ty = try pt.arrayType(.{
13575 .len = result_len,
13576 .sentinel = if (res_sent_val) |v| v.toIntern() else .none,
13577 .child = resolved_elem_ty.toIntern(),
13578 });
13579 const ptr_addrspace = p: {
13580 if (lhs_ty.zigTypeTag(zcu) == .pointer) break :p lhs_ty.ptrAddressSpace(zcu);
13581 if (rhs_ty.zigTypeTag(zcu) == .pointer) break :p rhs_ty.ptrAddressSpace(zcu);
13582 break :p null;
13583 };
13584
13585 const runtime_src = if (switch (lhs_ty.zigTypeTag(zcu)) {
13586 .array, .@"struct" => sema.resolveValue(lhs),
13587 .pointer => try sema.resolveDefinedValue(block, lhs_src, lhs),
13588 else => unreachable,
13589 }) |lhs_val| rs: {
13590 if (switch (rhs_ty.zigTypeTag(zcu)) {
13591 .array, .@"struct" => sema.resolveValue(rhs),
13592 .pointer => try sema.resolveDefinedValue(block, rhs_src, rhs),
13593 else => unreachable,
13594 }) |rhs_val| {
13595 const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu))
13596 try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :rs lhs_src
13597 else if (lhs_ty.isSlice(zcu))
13598 try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :rs lhs_src
13599 else
13600 lhs_val;
13601
13602 const rhs_sub_val = if (rhs_ty.isSinglePointer(zcu))
13603 try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty) orelse break :rs rhs_src
13604 else if (rhs_ty.isSlice(zcu))
13605 try sema.maybeDerefSliceAsArray(block, rhs_src, rhs_val) orelse break :rs rhs_src
13606 else
13607 rhs_val;
13608
13609 const element_vals = try sema.arena.alloc(InternPool.Index, result_len);
13610 var elem_i: u32 = 0;
13611 while (elem_i < lhs_len) : (elem_i += 1) {
13612 const lhs_elem_i = elem_i;
13613 const elem_default_val: ?Value = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else null;
13614 const elem_val = elem_default_val orelse
13615 if (lhs_sub_val.isUndef(zcu)) try pt.undefValue(resolved_elem_ty) else try lhs_sub_val.elemValue(pt, lhs_elem_i);
13616 const operand_src = block.src(.{ .array_cat_lhs = .{
13617 .array_cat_offset = inst_data.src_node,
13618 .elem_index = elem_i,
13619 } });
13620 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, .fromValue(elem_val), operand_src);
13621 const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?;
13622 element_vals[elem_i] = coerced_elem_val.toIntern();
13623 }
13624 while (elem_i < result_len) : (elem_i += 1) {
13625 const rhs_elem_i = elem_i - lhs_len;
13626 const elem_default_val: ?Value = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else null;
13627 const elem_val = elem_default_val orelse
13628 if (rhs_sub_val.isUndef(zcu)) try pt.undefValue(resolved_elem_ty) else try rhs_sub_val.elemValue(pt, rhs_elem_i);
13629 const operand_src = block.src(.{ .array_cat_rhs = .{
13630 .array_cat_offset = inst_data.src_node,
13631 .elem_index = @intCast(rhs_elem_i),
13632 } });
13633 const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, .fromValue(elem_val), operand_src);
13634 const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?;
13635 element_vals[elem_i] = coerced_elem_val.toIntern();
13636 }
13637 return sema.addConstantMaybeRef(
13638 try pt.aggregateValue(result_ty, element_vals),
13639 ptr_addrspace != null,
13640 );
13641 } else break :rs rhs_src;
13642 } else lhs_src;
13643
13644 try sema.requireRuntimeBlock(block, src, runtime_src);
13645
13646 if (ptr_addrspace) |ptr_as| {
13647 const constant_alloc_ty = try pt.ptrType(.{
13648 .child = result_ty.toIntern(),
13649 .flags = .{
13650 .address_space = ptr_as,
13651 .is_const = true,
13652 },
13653 });
13654 const alloc_ty = try pt.ptrType(.{
13655 .child = result_ty.toIntern(),
13656 .flags = .{ .address_space = ptr_as },
13657 });
13658 const elem_ptr_ty = try pt.ptrType(.{
13659 .child = resolved_elem_ty.toIntern(),
13660 .flags = .{ .address_space = ptr_as },
13661 });
13662
13663 const mutable_alloc = try block.addTy(.alloc, alloc_ty);
13664
13665 // if both the source and destination are arrays
13666 // we can hotpath via a memcpy.
13667 if (lhs_ty.zigTypeTag(zcu) == .pointer and
13668 rhs_ty.zigTypeTag(zcu) == .pointer)
13669 {
13670 const slice_ty = try pt.ptrType(.{
13671 .child = resolved_elem_ty.toIntern(),
13672 .flags = .{
13673 .size = .slice,
13674 .address_space = ptr_as,
13675 },
13676 });
13677
13678 const many_ty = slice_ty.slicePtrFieldType(zcu);
13679 const many_alloc = try block.addTyOp(.ptr_cast, many_ty, mutable_alloc);
13680
13681 // lhs_dest_slice = dest[0..lhs.len]
13682 if (lhs_len > 0) {
13683 const lhs_dest_slice = try block.addInst(.{
13684 .tag = .slice,
13685 .data = .{ .ty_pl = .{
13686 .ty = slice_ty,
13687 .payload = try sema.addExtra(Air.Bin{
13688 .lhs = many_alloc,
13689 .rhs = try pt.intRef(.usize, lhs_len),
13690 }),
13691 } },
13692 });
13693 _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs);
13694 }
13695
13696 // rhs_dest_slice = dest[lhs.len..][0..rhs.len]
13697 if (rhs_len > 0) {
13698 const rhs_dest_offset = try block.addInst(.{
13699 .tag = .ptr_add,
13700 .data = .{ .ty_pl = .{
13701 .ty = many_ty,
13702 .payload = try sema.addExtra(Air.Bin{
13703 .lhs = many_alloc,
13704 .rhs = try pt.intRef(.usize, lhs_len),
13705 }),
13706 } },
13707 });
13708 const rhs_dest_slice = try block.addInst(.{
13709 .tag = .slice,
13710 .data = .{ .ty_pl = .{
13711 .ty = slice_ty,
13712 .payload = try sema.addExtra(Air.Bin{
13713 .lhs = rhs_dest_offset,
13714 .rhs = try pt.intRef(.usize, rhs_len),
13715 }),
13716 } },
13717 });
13718 _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs);
13719 }
13720
13721 if (res_sent_val) |sent_val| {
13722 const elem_index = try pt.intRef(.usize, result_len);
13723 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
13724 const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern());
13725 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
13726 }
13727
13728 return block.addTyOp(.ptr_cast, constant_alloc_ty, mutable_alloc);
13729 }
13730
13731 var elem_i: u32 = 0;
13732 while (elem_i < lhs_len) : (elem_i += 1) {
13733 const elem_index = try pt.intRef(.usize, elem_i);
13734 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
13735 const operand_src = block.src(.{ .array_cat_lhs = .{
13736 .array_cat_offset = inst_data.src_node,
13737 .elem_index = elem_i,
13738 } });
13739 const init = try sema.elemVal(block, operand_src, lhs, elem_index, src, true);
13740 try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store);
13741 }
13742 while (elem_i < result_len) : (elem_i += 1) {
13743 const rhs_elem_i = elem_i - lhs_len;
13744 const elem_index = try pt.intRef(.usize, elem_i);
13745 const rhs_index = try pt.intRef(.usize, rhs_elem_i);
13746 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
13747 const operand_src = block.src(.{ .array_cat_rhs = .{
13748 .array_cat_offset = inst_data.src_node,
13749 .elem_index = @intCast(rhs_elem_i),
13750 } });
13751 const init = try sema.elemVal(block, operand_src, rhs, rhs_index, src, true);
13752 try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store);
13753 }
13754 if (res_sent_val) |sent_val| {
13755 const elem_index = try pt.intRef(.usize, result_len);
13756 const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty);
13757 const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern());
13758 try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store);
13759 }
13760
13761 return block.addTyOp(.ptr_cast, constant_alloc_ty, mutable_alloc);
13762 }
13763
13764 const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len);
13765 {
13766 var elem_i: u32 = 0;
13767 while (elem_i < lhs_len) : (elem_i += 1) {
13768 const index = try pt.intRef(.usize, elem_i);
13769 const operand_src = block.src(.{ .array_cat_lhs = .{
13770 .array_cat_offset = inst_data.src_node,
13771 .elem_index = elem_i,
13772 } });
13773 const init = try sema.elemVal(block, operand_src, lhs, index, src, true);
13774 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src);
13775 }
13776 while (elem_i < result_len) : (elem_i += 1) {
13777 const rhs_elem_i = elem_i - lhs_len;
13778 const index = try pt.intRef(.usize, rhs_elem_i);
13779 const operand_src = block.src(.{ .array_cat_rhs = .{
13780 .array_cat_offset = inst_data.src_node,
13781 .elem_index = @intCast(rhs_elem_i),
13782 } });
13783 const init = try sema.elemVal(block, operand_src, rhs, index, src, true);
13784 element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src);
13785 }
13786 }
13787
13788 return block.addAggregateInit(result_ty, element_refs);
13789}
13790
13791fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo {
13792 const pt = sema.pt;
13793 const zcu = pt.zcu;
13794 const operand_ty = sema.typeOf(operand);
13795 switch (operand_ty.zigTypeTag(zcu)) {
13796 .array => return operand_ty.arrayInfo(zcu),
13797 .pointer => {
13798 const ptr_info = operand_ty.ptrInfo(zcu);
13799 switch (ptr_info.flags.size) {
13800 .slice => {
13801 const val = try sema.resolveConstDefinedValue(block, src, operand, .{ .simple = .slice_cat_operand });
13802 return .{
13803 .elem_type = .fromInterned(ptr_info.child),
13804 .sentinel = switch (ptr_info.sentinel) {
13805 .none => null,
13806 else => Value.fromInterned(ptr_info.sentinel),
13807 },
13808 .len = val.sliceLen(zcu),
13809 };
13810 },
13811 .one => {
13812 if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) {
13813 return Type.fromInterned(ptr_info.child).arrayInfo(zcu);
13814 }
13815 },
13816 .c, .many => {},
13817 }
13818 },
13819 .@"struct" => {
13820 if (operand_ty.isTuple(zcu) and peer_ty.isIndexable(zcu)) {
13821 assert(!peer_ty.isTuple(zcu));
13822 const peer_elem_ty = switch (peer_ty.zigTypeTag(zcu)) {
13823 .pointer => switch (peer_ty.ptrSize(zcu)) {
13824 .one => switch (peer_ty.childType(zcu).zigTypeTag(zcu)) {
13825 .array, .vector => peer_ty.childType(zcu).childType(zcu),
13826 .@"struct" => return null,
13827 else => unreachable,
13828 },
13829 .many, .c, .slice => peer_ty.childType(zcu),
13830 },
13831 .vector, .array => peer_ty.childType(zcu),
13832 else => unreachable,
13833 };
13834 return .{
13835 .elem_type = peer_elem_ty,
13836 .sentinel = null,
13837 .len = operand_ty.arrayLen(zcu),
13838 };
13839 }
13840 },
13841 else => {},
13842 }
13843 return null;
13844}
13845
13846fn analyzeTupleMul(
13847 sema: *Sema,
13848 block: *Block,
13849 src_node: std.zig.Ast.Node.Offset,
13850 operand: Air.Inst.Ref,
13851 factor: usize,
13852) CompileError!Air.Inst.Ref {
13853 const pt = sema.pt;
13854 const zcu = pt.zcu;
13855 const comp = zcu.comp;
13856 const gpa = comp.gpa;
13857 const io = comp.io;
13858
13859 const operand_ty = sema.typeOf(operand);
13860 const src = block.nodeOffset(src_node);
13861 const len_src = block.src(.{ .node_offset_bin_rhs = src_node });
13862
13863 const tuple_len = operand_ty.structFieldCount(zcu);
13864 const final_len = std.math.mul(usize, tuple_len, factor) catch
13865 return sema.fail(block, len_src, "operation results in overflow", .{});
13866
13867 if (final_len == 0) {
13868 return .empty_tuple;
13869 }
13870 const types = try sema.arena.alloc(InternPool.Index, final_len);
13871 const values = try sema.arena.alloc(InternPool.Index, final_len);
13872
13873 const opt_runtime_src = rs: {
13874 var runtime_src: ?LazySrcLoc = null;
13875 for (0..tuple_len) |i| {
13876 types[i] = operand_ty.fieldType(i, zcu).toIntern();
13877 const operand_src = block.src(.{ .array_cat_lhs = .{
13878 .array_cat_offset = src_node,
13879 .elem_index = @intCast(i),
13880 } });
13881 if (operand_ty.structFieldDefaultValue(i, zcu)) |default_val| {
13882 values[i] = default_val.toIntern();
13883 } else {
13884 runtime_src = operand_src;
13885 values[i] = .none; // TODO don't treat unreachable_value as special
13886 }
13887 }
13888 for (0..factor) |i| {
13889 @memmove(types[tuple_len * i ..][0..tuple_len], types[0..tuple_len]);
13890 @memmove(values[tuple_len * i ..][0..tuple_len], values[0..tuple_len]);
13891 }
13892 break :rs runtime_src;
13893 };
13894
13895 const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{
13896 .types = types,
13897 .values = values,
13898 }));
13899
13900 const runtime_src = opt_runtime_src orelse {
13901 const tuple_val = try pt.aggregateValue(tuple_ty, values);
13902 return Air.internedToRef(tuple_val.toIntern());
13903 };
13904
13905 try sema.requireRuntimeBlock(block, src, runtime_src);
13906
13907 const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len);
13908 var i: u32 = 0;
13909 while (i < tuple_len) : (i += 1) {
13910 element_refs[i] = try sema.tupleFieldValByIndex(block, operand, @intCast(i), operand_ty);
13911 }
13912 i = 1;
13913 while (i < factor) : (i += 1) {
13914 @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]);
13915 }
13916
13917 return block.addAggregateInit(tuple_ty, element_refs);
13918}
13919
13920fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
13921 const pt = sema.pt;
13922 const zcu = pt.zcu;
13923 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
13924 const src = block.nodeOffset(inst_data.src_node);
13925 const lhs_src = src;
13926 const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
13927
13928 const rhs = sema.resolveInst(inst_data.operand);
13929 const rhs_ty = sema.typeOf(rhs);
13930 const rhs_scalar_ty = rhs_ty.scalarType(zcu);
13931
13932 if (rhs_scalar_ty.isUnsignedInt(zcu) or switch (rhs_scalar_ty.zigTypeTag(zcu)) {
13933 .int, .comptime_int, .float, .comptime_float => false,
13934 else => true,
13935 }) {
13936 return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)});
13937 }
13938
13939 if (rhs_scalar_ty.isAnyFloat()) {
13940 // We handle float negation here to ensure negative zero is represented in the bits.
13941 if (sema.resolveValue(rhs)) |rhs_val| {
13942 const result = try arith.negateFloat(sema, rhs_ty, rhs_val);
13943 return Air.internedToRef(result.toIntern());
13944 }
13945 try sema.requireRuntimeBlock(block, src, null);
13946 return block.addUnOp(if (block.float_mode == .optimized) .neg_optimized else .neg, rhs);
13947 }
13948
13949 const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern());
13950 return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true);
13951}
13952
13953fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
13954 const pt = sema.pt;
13955 const zcu = pt.zcu;
13956 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
13957 const src = block.nodeOffset(inst_data.src_node);
13958 const lhs_src = src;
13959 const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
13960
13961 const rhs = sema.resolveInst(inst_data.operand);
13962 const rhs_ty = sema.typeOf(rhs);
13963 const rhs_scalar_ty = rhs_ty.scalarType(zcu);
13964
13965 switch (rhs_scalar_ty.zigTypeTag(zcu)) {
13966 .int, .comptime_int, .float, .comptime_float => {},
13967 else => return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)}),
13968 }
13969
13970 const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern());
13971 return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true);
13972}
13973
13974fn zirArithmetic(
13975 sema: *Sema,
13976 block: *Block,
13977 inst: Zir.Inst.Index,
13978 zir_tag: Zir.Inst.Tag,
13979 safety: bool,
13980) CompileError!Air.Inst.Ref {
13981 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
13982 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
13983 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
13984 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
13985 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
13986 const lhs = sema.resolveInst(extra.lhs);
13987 const rhs = sema.resolveInst(extra.rhs);
13988
13989 return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, src, lhs_src, rhs_src, safety);
13990}
13991
13992fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
13993 const pt = sema.pt;
13994 const zcu = pt.zcu;
13995 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
13996 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
13997 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
13998 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
13999 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14000 const lhs = sema.resolveInst(extra.lhs);
14001 const rhs = sema.resolveInst(extra.rhs);
14002 const lhs_ty = sema.typeOf(lhs);
14003 const rhs_ty = sema.typeOf(rhs);
14004 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14005 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14006 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14007 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14008
14009 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14010 .override = &.{ lhs_src, rhs_src },
14011 });
14012
14013 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14014 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14015
14016 const lhs_scalar_ty = lhs_ty.scalarType(zcu);
14017 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14018
14019 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14020
14021 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div);
14022
14023 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14024 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14025
14026 if ((lhs_ty.zigTypeTag(zcu) == .comptime_float and rhs_ty.zigTypeTag(zcu) == .comptime_int) or
14027 (lhs_ty.zigTypeTag(zcu) == .comptime_int and rhs_ty.zigTypeTag(zcu) == .comptime_float))
14028 {
14029 // If it makes a difference whether we coerce to ints or floats before doing the division, error.
14030 // If lhs % rhs is 0, it doesn't matter.
14031 const lhs_val = maybe_lhs_val orelse unreachable;
14032 const rhs_val = maybe_rhs_val orelse unreachable;
14033 const rem = arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem) catch unreachable;
14034 if (!rem.compareAllWithZero(.eq, zcu)) {
14035 return sema.fail(
14036 block,
14037 src,
14038 "ambiguous coercion of division operands '{f}' and '{f}'; non-zero remainder '{f}'",
14039 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt), rem.fmtValueSema(pt, sema) },
14040 );
14041 }
14042 }
14043
14044 // TODO: emit compile error when .div is used on integers and there would be an
14045 // ambiguous result between div_floor and div_trunc.
14046
14047 // The rules here are like those in `analyzeArithmetic`:
14048 //
14049 // * If both operands are comptime-known, call the corresponding function in `arith`.
14050 // Inputs which would be IB at runtime are compile errors.
14051 //
14052 // * Otherwise, if one operand is comptime-known `undefined`, we either trigger a compile error
14053 // or return `undefined`, depending on whether this operator can trigger IB.
14054 //
14055 // * No other comptime operand determines a comptime result, so remaining cases are runtime ops.
14056
14057 const allow_div_zero = !is_int and
14058 resolved_type.toIntern() != .comptime_float_type and
14059 block.float_mode == .strict;
14060
14061 if (maybe_lhs_val) |lhs_val| {
14062 if (maybe_rhs_val) |rhs_val| {
14063 return .fromValue(try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div));
14064 }
14065 if (allow_div_zero) {
14066 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14067 } else {
14068 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14069 }
14070 } else if (maybe_rhs_val) |rhs_val| {
14071 if (allow_div_zero) {
14072 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14073 } else {
14074 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14075 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14076 }
14077 }
14078
14079 if (block.wantSafety()) {
14080 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
14081 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14082 }
14083
14084 const air_tag: Air.Inst.Tag = if (is_int) blk: {
14085 if (lhs_ty.isSignedInt(zcu) or rhs_ty.isSignedInt(zcu)) {
14086 return sema.fail(
14087 block,
14088 src,
14089 "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact",
14090 .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) },
14091 );
14092 }
14093 break :blk .div_trunc;
14094 } else switch (block.float_mode) {
14095 .optimized => .div_float_optimized,
14096 .strict => .div_float,
14097 };
14098 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
14099}
14100
14101fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14102 const pt = sema.pt;
14103 const zcu = pt.zcu;
14104 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
14105 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
14106 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
14107 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
14108 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14109 const lhs = sema.resolveInst(extra.lhs);
14110 const rhs = sema.resolveInst(extra.rhs);
14111 const lhs_ty = sema.typeOf(lhs);
14112 const rhs_ty = sema.typeOf(rhs);
14113 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14114 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14115 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14116 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14117
14118 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14119 .override = &.{ lhs_src, rhs_src },
14120 });
14121
14122 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14123 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14124
14125 const lhs_scalar_ty = lhs_ty.scalarType(zcu);
14126 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14127
14128 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14129
14130 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact);
14131
14132 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14133 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14134
14135 // Because `@divExact` can trigger Illegal Behavior, undefined operands trigger Illegal Behavior.
14136
14137 if (maybe_lhs_val) |lhs_val| {
14138 if (maybe_rhs_val) |rhs_val| {
14139 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_exact);
14140 return Air.internedToRef(result.toIntern());
14141 }
14142 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14143 } else if (maybe_rhs_val) |rhs_val| {
14144 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14145 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14146 }
14147
14148 // Depending on whether safety is enabled, we will have a slightly different strategy
14149 // here. The `div_exact` AIR instruction causes illegal behavior if a remainder
14150 // is produced, so in the safety check case, it cannot be used. Instead we do a
14151 // div_trunc and check for remainder.
14152
14153 if (block.wantSafety()) {
14154 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
14155 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14156
14157 const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs);
14158 const ok = if (!is_int) ok: {
14159 const floored = try block.addUnOp(.floor, result);
14160
14161 if (resolved_type.zigTypeTag(zcu) == .vector) {
14162 const eql = try block.addCmpVector(result, floored, .eq);
14163 break :ok try block.addReduce(eql, .And);
14164 } else {
14165 const is_in_range = try block.addBinOp(switch (block.float_mode) {
14166 .strict => .cmp_eq,
14167 .optimized => .cmp_eq_optimized,
14168 }, result, floored);
14169 break :ok is_in_range;
14170 }
14171 } else ok: {
14172 const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
14173
14174 const scalar_zero = switch (scalar_tag) {
14175 .comptime_float, .float => try pt.floatValue(resolved_type.scalarType(zcu), 0.0),
14176 .comptime_int, .int => try pt.intValue(resolved_type.scalarType(zcu), 0),
14177 else => unreachable,
14178 };
14179 if (resolved_type.zigTypeTag(zcu) == .vector) {
14180 const zero_val = try sema.splat(resolved_type, scalar_zero);
14181 const zero = Air.internedToRef(zero_val.toIntern());
14182 const eql = try block.addCmpVector(remainder, zero, .eq);
14183 break :ok try block.addReduce(eql, .And);
14184 } else {
14185 const zero = Air.internedToRef(scalar_zero.toIntern());
14186 const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero);
14187 break :ok is_in_range;
14188 }
14189 };
14190 try sema.addSafetyCheck(block, src, ok, .exact_division_remainder);
14191 return result;
14192 }
14193
14194 return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs);
14195}
14196
14197fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14198 const pt = sema.pt;
14199 const zcu = pt.zcu;
14200 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
14201 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
14202 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
14203 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
14204 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14205 const lhs = sema.resolveInst(extra.lhs);
14206 const rhs = sema.resolveInst(extra.rhs);
14207 const lhs_ty = sema.typeOf(lhs);
14208 const rhs_ty = sema.typeOf(rhs);
14209 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14210 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14211 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14212 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14213
14214 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14215 .override = &.{ lhs_src, rhs_src },
14216 });
14217
14218 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14219 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14220
14221 const lhs_scalar_ty = lhs_ty.scalarType(zcu);
14222 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14223
14224 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14225
14226 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor);
14227
14228 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14229 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14230
14231 const allow_div_zero = !is_int and
14232 resolved_type.toIntern() != .comptime_float_type and
14233 block.float_mode == .strict;
14234
14235 if (maybe_lhs_val) |lhs_val| {
14236 if (maybe_rhs_val) |rhs_val| {
14237 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_floor);
14238 return Air.internedToRef(result.toIntern());
14239 }
14240 if (allow_div_zero) {
14241 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14242 } else {
14243 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14244 }
14245 } else if (maybe_rhs_val) |rhs_val| {
14246 if (allow_div_zero) {
14247 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14248 } else {
14249 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14250 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14251 }
14252 }
14253
14254 if (block.wantSafety()) {
14255 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
14256 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14257 }
14258
14259 return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
14260}
14261
14262fn zirDivCeil(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14263 const pt = sema.pt;
14264 const zcu = pt.zcu;
14265 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
14266 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
14267 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
14268 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
14269 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14270 const lhs = sema.resolveInst(extra.lhs);
14271 const rhs = sema.resolveInst(extra.rhs);
14272 const lhs_ty = sema.typeOf(lhs);
14273 const rhs_ty = sema.typeOf(rhs);
14274 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14275 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14276 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14277 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14278
14279 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14280 .override = &.{ lhs_src, rhs_src },
14281 });
14282
14283 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14284 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14285
14286 const lhs_scalar_ty = lhs_ty.scalarType(zcu);
14287 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14288
14289 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14290
14291 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_ceil);
14292
14293 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14294 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14295
14296 const allow_div_zero = !is_int and
14297 resolved_type.toIntern() != .comptime_float_type and
14298 block.float_mode == .strict;
14299
14300 if (maybe_lhs_val) |lhs_val| {
14301 if (maybe_rhs_val) |rhs_val| {
14302 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_ceil);
14303 return Air.internedToRef(result.toIntern());
14304 }
14305 if (allow_div_zero) {
14306 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14307 } else {
14308 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14309 }
14310 } else if (maybe_rhs_val) |rhs_val| {
14311 if (allow_div_zero) {
14312 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14313 } else {
14314 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14315 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14316 }
14317 }
14318
14319 if (block.wantSafety()) {
14320 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
14321 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14322 }
14323
14324 return block.addBinOp(airTag(block, is_int, .div_ceil, .div_ceil_optimized), casted_lhs, casted_rhs);
14325}
14326
14327fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14328 const pt = sema.pt;
14329 const zcu = pt.zcu;
14330 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
14331 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
14332 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
14333 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
14334 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14335 const lhs = sema.resolveInst(extra.lhs);
14336 const rhs = sema.resolveInst(extra.rhs);
14337 const lhs_ty = sema.typeOf(lhs);
14338 const rhs_ty = sema.typeOf(rhs);
14339 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14340 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14341 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14342 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14343
14344 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14345 .override = &.{ lhs_src, rhs_src },
14346 });
14347
14348 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14349 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14350
14351 const lhs_scalar_ty = lhs_ty.scalarType(zcu);
14352 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14353
14354 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14355
14356 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc);
14357
14358 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14359 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14360
14361 const allow_div_zero = !is_int and
14362 resolved_type.toIntern() != .comptime_float_type and
14363 block.float_mode == .strict;
14364
14365 if (maybe_lhs_val) |lhs_val| {
14366 if (maybe_rhs_val) |rhs_val| {
14367 const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_trunc);
14368 return Air.internedToRef(result.toIntern());
14369 }
14370 if (allow_div_zero) {
14371 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14372 } else {
14373 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14374 }
14375 } else if (maybe_rhs_val) |rhs_val| {
14376 if (allow_div_zero) {
14377 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14378 } else {
14379 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14380 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14381 }
14382 }
14383
14384 if (block.wantSafety()) {
14385 try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
14386 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14387 }
14388
14389 return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs);
14390}
14391
14392fn addDivIntOverflowSafety(
14393 sema: *Sema,
14394 block: *Block,
14395 src: LazySrcLoc,
14396 resolved_type: Type,
14397 lhs_scalar_ty: Type,
14398 maybe_lhs_val: ?Value,
14399 maybe_rhs_val: ?Value,
14400 casted_lhs: Air.Inst.Ref,
14401 casted_rhs: Air.Inst.Ref,
14402 is_int: bool,
14403) CompileError!void {
14404 const pt = sema.pt;
14405 const zcu = pt.zcu;
14406 if (!is_int) return;
14407
14408 // If the LHS is unsigned, it cannot cause overflow.
14409 if (!lhs_scalar_ty.isSignedInt(zcu)) return;
14410
14411 // If the LHS is widened to a larger integer type, no overflow is possible.
14412 if (lhs_scalar_ty.intInfo(zcu).bits < resolved_type.intInfo(zcu).bits) {
14413 return;
14414 }
14415
14416 const min_int = try resolved_type.minInt(pt, resolved_type);
14417 const neg_one_scalar = try pt.intValue(lhs_scalar_ty, -1);
14418 const neg_one = try sema.splat(resolved_type, neg_one_scalar);
14419
14420 // If the LHS is comptime-known to be not equal to the min int,
14421 // no overflow is possible.
14422 if (maybe_lhs_val) |lhs_val| {
14423 if (try lhs_val.compareAll(.neq, min_int, resolved_type, pt)) return;
14424 }
14425
14426 // If the RHS is comptime-known to not be equal to -1, no overflow is possible.
14427 if (maybe_rhs_val) |rhs_val| {
14428 if (try rhs_val.compareAll(.neq, neg_one, resolved_type, pt)) return;
14429 }
14430
14431 if (resolved_type.zigTypeTag(zcu) == .vector) {
14432 const vec_len = resolved_type.vectorLen(zcu);
14433
14434 // This is a bool vector whose elements are true if the LHS element does NOT equal `min_int`.
14435 const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val) |lhs_val| ok: {
14436 // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements.
14437 const min_int_scalar = try min_int.elemValue(pt, 0);
14438 const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len);
14439 for (elems_ok, 0..) |*elem_ok, elem_idx| {
14440 const elem_val = try lhs_val.elemValue(pt, elem_idx);
14441 elem_ok.* = if (elem_val.eqlScalarNum(min_int_scalar, zcu)) .bool_false else .bool_true;
14442 }
14443 break :ok .fromValue(try pt.aggregateValue(try pt.vectorType(.{
14444 .len = vec_len,
14445 .child = .bool_type,
14446 }), elems_ok));
14447 } else ok: {
14448 // The operand isn't comptime-known; add a runtime comparison.
14449 const min_int_ref = Air.internedToRef(min_int.toIntern());
14450 break :ok try block.addCmpVector(casted_lhs, min_int_ref, .neq);
14451 };
14452
14453 // This is a bool vector whose elements are true if the RHS element does NOT equal -1.
14454 const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val) |rhs_val| ok: {
14455 // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements.
14456 const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len);
14457 for (elems_ok, 0..) |*elem_ok, elem_idx| {
14458 const elem_val = try rhs_val.elemValue(pt, elem_idx);
14459 elem_ok.* = if (elem_val.eqlScalarNum(neg_one_scalar, zcu)) .bool_false else .bool_true;
14460 }
14461 break :ok .fromValue(try pt.aggregateValue(try pt.vectorType(.{
14462 .len = vec_len,
14463 .child = .bool_type,
14464 }), elems_ok));
14465 } else ok: {
14466 // The operand isn't comptime-known; add a runtime comparison.
14467 const neg_one_ref = Air.internedToRef(neg_one.toIntern());
14468 break :ok try block.addCmpVector(casted_rhs, neg_one_ref, .neq);
14469 };
14470
14471 const ok = try block.addReduce(try block.addBinOp(.bit_or, lhs_ok, rhs_ok), .And);
14472 try sema.addSafetyCheck(block, src, ok, .integer_overflow);
14473 } else {
14474 const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val == null) ok: {
14475 const min_int_ref = Air.internedToRef(min_int.toIntern());
14476 break :ok try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref);
14477 } else .none; // means false
14478 const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val == null) ok: {
14479 const neg_one_ref = Air.internedToRef(neg_one.toIntern());
14480 break :ok try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref);
14481 } else .none; // means false
14482
14483 const ok = if (lhs_ok != .none and rhs_ok != .none)
14484 try block.addBinOp(.bit_or, lhs_ok, rhs_ok)
14485 else if (lhs_ok != .none)
14486 lhs_ok
14487 else if (rhs_ok != .none)
14488 rhs_ok
14489 else
14490 unreachable;
14491
14492 try sema.addSafetyCheck(block, src, ok, .integer_overflow);
14493 }
14494}
14495
14496fn addDivByZeroSafety(
14497 sema: *Sema,
14498 block: *Block,
14499 src: LazySrcLoc,
14500 resolved_type: Type,
14501 maybe_rhs_val: ?Value,
14502 casted_rhs: Air.Inst.Ref,
14503 is_int: bool,
14504) CompileError!void {
14505 // Strict IEEE floats have well-defined division by zero.
14506 if (!is_int and block.float_mode == .strict) return;
14507
14508 // If rhs was comptime-known to be zero a compile error would have been
14509 // emitted above.
14510 if (maybe_rhs_val != null) return;
14511
14512 const pt = sema.pt;
14513 const zcu = pt.zcu;
14514 const scalar_zero = if (is_int)
14515 try pt.intValue(resolved_type.scalarType(zcu), 0)
14516 else
14517 try pt.floatValue(resolved_type.scalarType(zcu), 0.0);
14518 const ok = if (resolved_type.zigTypeTag(zcu) == .vector) ok: {
14519 const zero_val = try sema.splat(resolved_type, scalar_zero);
14520 const zero = Air.internedToRef(zero_val.toIntern());
14521 const ok = try block.addCmpVector(casted_rhs, zero, .neq);
14522 break :ok try block.addReduce(ok, .And);
14523 } else ok: {
14524 const zero = Air.internedToRef(scalar_zero.toIntern());
14525 break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero);
14526 };
14527 try sema.addSafetyCheck(block, src, ok, .divide_by_zero);
14528}
14529
14530fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag {
14531 if (is_int) return normal;
14532 return switch (block.float_mode) {
14533 .strict => normal,
14534 .optimized => optimized,
14535 };
14536}
14537
14538fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14539 const pt = sema.pt;
14540 const zcu = pt.zcu;
14541 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
14542 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
14543 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
14544 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
14545 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14546 const lhs = sema.resolveInst(extra.lhs);
14547 const rhs = sema.resolveInst(extra.rhs);
14548 const lhs_ty = sema.typeOf(lhs);
14549 const rhs_ty = sema.typeOf(rhs);
14550 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14551 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14552 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14553 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14554
14555 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14556 .override = &.{ lhs_src, rhs_src },
14557 });
14558
14559 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14560 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14561
14562 const lhs_scalar_ty = lhs_ty.scalarType(zcu);
14563 const rhs_scalar_ty = rhs_ty.scalarType(zcu);
14564 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14565
14566 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14567
14568 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem);
14569
14570 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14571 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14572
14573 const lhs_maybe_negative = a: {
14574 if (lhs_scalar_ty.isUnsignedInt(zcu)) break :a false;
14575 const lhs_val = maybe_lhs_val orelse break :a true;
14576 if (lhs_val.compareAllWithZero(.gte, zcu)) break :a false;
14577 break :a true;
14578 };
14579 const rhs_maybe_negative = a: {
14580 if (rhs_scalar_ty.isUnsignedInt(zcu)) break :a false;
14581 const rhs_val = maybe_rhs_val orelse break :a true;
14582 if (rhs_val.compareAllWithZero(.gte, zcu)) break :a false;
14583 break :a true;
14584 };
14585
14586 if (maybe_lhs_val) |lhs_val| {
14587 if (maybe_rhs_val) |rhs_val| {
14588 const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem);
14589 if (lhs_maybe_negative or rhs_maybe_negative) {
14590 if (!result.compareAllWithZero(.eq, zcu)) {
14591 // Non-zero result means ambiguity between mod and rem
14592 return sema.failWithModRemNegative(block, src: {
14593 if (lhs_maybe_negative) break :src lhs_src;
14594 if (rhs_maybe_negative) break :src rhs_src;
14595 unreachable;
14596 }, lhs_ty, rhs_ty);
14597 }
14598 }
14599 return Air.internedToRef(result.toIntern());
14600 }
14601 }
14602
14603 // Result not comptime-known, so floats and signed integers are illegal due to mod/rem ambiguity
14604 if (lhs_maybe_negative or rhs_maybe_negative) {
14605 return sema.failWithModRemNegative(block, src: {
14606 if (lhs_maybe_negative) break :src lhs_src;
14607 if (rhs_maybe_negative) break :src rhs_src;
14608 unreachable;
14609 }, lhs_ty, rhs_ty);
14610 }
14611
14612 const allow_div_zero = !is_int and
14613 resolved_type.toIntern() != .comptime_float_type and
14614 block.float_mode == .strict;
14615
14616 if (maybe_lhs_val) |lhs_val| {
14617 if (allow_div_zero) {
14618 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14619 } else {
14620 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14621 }
14622 } else if (maybe_rhs_val) |rhs_val| {
14623 if (allow_div_zero) {
14624 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14625 } else {
14626 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14627 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14628 }
14629 }
14630
14631 if (block.wantSafety()) {
14632 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14633 }
14634
14635 const air_tag = airTag(block, is_int, .rem, .rem_optimized);
14636 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
14637}
14638
14639fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14640 const pt = sema.pt;
14641 const zcu = pt.zcu;
14642 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
14643 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
14644 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
14645 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
14646 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14647 const lhs = sema.resolveInst(extra.lhs);
14648 const rhs = sema.resolveInst(extra.rhs);
14649 const lhs_ty = sema.typeOf(lhs);
14650 const rhs_ty = sema.typeOf(rhs);
14651 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14652 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14653 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14654 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14655
14656 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14657 .override = &.{ lhs_src, rhs_src },
14658 });
14659
14660 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14661 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14662
14663 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14664
14665 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14666
14667 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod);
14668
14669 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14670 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14671
14672 const allow_div_zero = !is_int and
14673 resolved_type.toIntern() != .comptime_float_type and
14674 block.float_mode == .strict;
14675
14676 if (maybe_lhs_val) |lhs_val| {
14677 if (maybe_rhs_val) |rhs_val| {
14678 const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .mod);
14679 return Air.internedToRef(result.toIntern());
14680 }
14681 if (allow_div_zero) {
14682 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14683 } else {
14684 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14685 }
14686 } else if (maybe_rhs_val) |rhs_val| {
14687 if (allow_div_zero) {
14688 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14689 } else {
14690 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14691 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14692 }
14693 }
14694
14695 if (block.wantSafety()) {
14696 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14697 }
14698
14699 const air_tag = airTag(block, is_int, .mod, .mod_optimized);
14700 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
14701}
14702
14703fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
14704 const pt = sema.pt;
14705 const zcu = pt.zcu;
14706 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
14707 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
14708 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
14709 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
14710 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
14711 const lhs = sema.resolveInst(extra.lhs);
14712 const rhs = sema.resolveInst(extra.rhs);
14713 const lhs_ty = sema.typeOf(lhs);
14714 const rhs_ty = sema.typeOf(rhs);
14715 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
14716 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
14717 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14718 try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty);
14719
14720 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
14721 .override = &.{ lhs_src, rhs_src },
14722 });
14723
14724 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
14725 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
14726
14727 const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu);
14728
14729 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
14730
14731 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem);
14732
14733 const maybe_lhs_val = sema.resolveValue(casted_lhs);
14734 const maybe_rhs_val = sema.resolveValue(casted_rhs);
14735
14736 const allow_div_zero = !is_int and
14737 resolved_type.toIntern() != .comptime_float_type and
14738 block.float_mode == .strict;
14739
14740 if (maybe_lhs_val) |lhs_val| {
14741 if (maybe_rhs_val) |rhs_val| {
14742 const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem);
14743 return Air.internedToRef(result.toIntern());
14744 }
14745 if (allow_div_zero) {
14746 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14747 } else {
14748 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14749 }
14750 } else if (maybe_rhs_val) |rhs_val| {
14751 if (allow_div_zero) {
14752 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
14753 } else {
14754 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
14755 if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src);
14756 }
14757 }
14758
14759 if (block.wantSafety()) {
14760 try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int);
14761 }
14762
14763 const air_tag = airTag(block, is_int, .rem, .rem_optimized);
14764 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
14765}
14766
14767fn zirOverflowArithmetic(
14768 sema: *Sema,
14769 block: *Block,
14770 extended: Zir.Inst.Extended.InstData,
14771 zir_tag: Zir.Inst.Extended,
14772) CompileError!Air.Inst.Ref {
14773 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
14774 const src = block.nodeOffset(extra.node);
14775
14776 const lhs_src = block.builtinCallArgSrc(extra.node, 0);
14777 const rhs_src = block.builtinCallArgSrc(extra.node, 1);
14778
14779 const uncasted_lhs = sema.resolveInst(extra.lhs);
14780 const uncasted_rhs = sema.resolveInst(extra.rhs);
14781
14782 const lhs_ty = sema.typeOf(uncasted_lhs);
14783 const rhs_ty = sema.typeOf(uncasted_rhs);
14784 const pt = sema.pt;
14785 const zcu = pt.zcu;
14786 const ip = &zcu.intern_pool;
14787
14788 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
14789
14790 const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs };
14791 const dest_ty = if (zir_tag == .shl_with_overflow)
14792 lhs_ty
14793 else
14794 try sema.resolvePeerTypes(block, src, instructions, .{
14795 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
14796 });
14797
14798 const rhs_dest_ty = if (zir_tag == .shl_with_overflow)
14799 try sema.log2IntType(block, lhs_ty, src)
14800 else
14801 dest_ty;
14802
14803 const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src);
14804 const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src);
14805
14806 if (dest_ty.scalarType(zcu).zigTypeTag(zcu) != .int) {
14807 return sema.fail(block, src, "expected vector of integers or integer tag type, found '{f}'", .{dest_ty.fmt(pt)});
14808 }
14809
14810 const maybe_lhs_val = sema.resolveValue(lhs);
14811 const maybe_rhs_val = sema.resolveValue(rhs);
14812
14813 const tuple_ty = try pt.overflowArithmeticTupleType(dest_ty);
14814 const overflow_ty: Type = .fromInterned(ip.indexToKey(tuple_ty.toIntern()).tuple_type.types.get(ip)[1]);
14815
14816 var result: struct {
14817 inst: Air.Inst.Ref = .none,
14818 wrapped: Value = Value.@"unreachable",
14819 overflow_bit: Value,
14820 } = result: {
14821 switch (zir_tag) {
14822 .add_with_overflow => {
14823 // If either of the arguments is zero, `false` is returned and the other is stored
14824 // to the result, even if it is undefined..
14825 // Otherwise, if either of the argument is undefined, undefined is returned.
14826 if (maybe_lhs_val) |lhs_val| {
14827 if (!lhs_val.isUndef(zcu) and lhs_val.compareAllWithZero(.eq, zcu)) {
14828 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
14829 }
14830 }
14831 if (maybe_rhs_val) |rhs_val| {
14832 if (!rhs_val.isUndef(zcu) and rhs_val.compareAllWithZero(.eq, zcu)) {
14833 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
14834 }
14835 }
14836 if (maybe_lhs_val) |lhs_val| {
14837 if (maybe_rhs_val) |rhs_val| {
14838 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
14839 break :result .{ .overflow_bit = .undef, .wrapped = .undef };
14840 }
14841
14842 const result = try arith.addWithOverflow(sema, dest_ty, lhs_val, rhs_val);
14843 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
14844 }
14845 }
14846 },
14847 .sub_with_overflow => {
14848 // If the rhs is zero, then the result is lhs and no overflow occured.
14849 // Otherwise, if either result is undefined, both results are undefined.
14850 if (maybe_rhs_val) |rhs_val| {
14851 if (rhs_val.isUndef(zcu)) {
14852 break :result .{ .overflow_bit = .undef, .wrapped = .undef };
14853 } else if (rhs_val.compareAllWithZero(.eq, zcu)) {
14854 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
14855 } else if (maybe_lhs_val) |lhs_val| {
14856 if (lhs_val.isUndef(zcu)) {
14857 break :result .{ .overflow_bit = .undef, .wrapped = .undef };
14858 }
14859
14860 const result = try arith.subWithOverflow(sema, dest_ty, lhs_val, rhs_val);
14861 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
14862 }
14863 }
14864 },
14865 .mul_with_overflow => {
14866 // If either of the arguments is zero, the result is zero and no overflow occured.
14867 if (maybe_lhs_val) |lhs_val| {
14868 if (!lhs_val.isUndef(zcu) and lhs_val.compareAllWithZero(.eq, zcu)) {
14869 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
14870 }
14871 }
14872 if (maybe_rhs_val) |rhs_val| {
14873 if (!rhs_val.isUndef(zcu) and rhs_val.compareAllWithZero(.eq, zcu)) {
14874 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
14875 }
14876 }
14877 // If either of the arguments is one, the result is the other and no overflow occured.
14878 const dest_scalar_ty = dest_ty.scalarType(zcu);
14879 const dest_scalar_int = dest_scalar_ty.intInfo(zcu);
14880 // We could still be working with i1, where '1' is not a legal value!
14881 if (!(dest_scalar_int.bits == 1 and dest_scalar_int.signedness == .signed)) {
14882 const scalar_one = try pt.intValue(dest_scalar_ty, 1);
14883 const vec_one = try sema.splat(dest_ty, scalar_one);
14884 if (maybe_lhs_val) |lhs_val| {
14885 if (!lhs_val.isUndef(zcu) and try sema.compareAll(lhs_val, .eq, vec_one, dest_ty)) {
14886 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs };
14887 }
14888 }
14889 if (maybe_rhs_val) |rhs_val| {
14890 if (!rhs_val.isUndef(zcu) and try sema.compareAll(rhs_val, .eq, vec_one, dest_ty)) {
14891 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
14892 }
14893 }
14894 }
14895
14896 if (maybe_lhs_val) |lhs_val| {
14897 if (maybe_rhs_val) |rhs_val| {
14898 if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) {
14899 break :result .{ .overflow_bit = .undef, .wrapped = .undef };
14900 }
14901 const result = try arith.mulWithOverflow(sema, dest_ty, lhs_val, rhs_val);
14902 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
14903 }
14904 }
14905 },
14906 .shl_with_overflow => {
14907 // If either of the arguments is undefined, IB is possible and we return an error.
14908 // If lhs is zero, the result is zero and no overflow occurred.
14909 // If rhs is zero, the result is lhs and no overflow occurred.
14910 const scalar_ty = lhs_ty.scalarType(zcu);
14911 if (maybe_rhs_val) |rhs_val| {
14912 if (maybe_lhs_val) |lhs_val| {
14913 const result = try arith.shlWithOverflow(sema, block, lhs_ty, lhs_val, rhs_val, lhs_src, rhs_src);
14914 break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result };
14915 }
14916 if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, null);
14917 const bits = scalar_ty.intInfo(zcu).bits;
14918 switch (rhs_ty.zigTypeTag(zcu)) {
14919 .int, .comptime_int => {
14920 switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
14921 .gt => {
14922 var rhs_space: Value.BigIntSpace = undefined;
14923 const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu);
14924 if (rhs_bigint.orderAgainstScalar(bits) != .lt) {
14925 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null);
14926 }
14927 },
14928 .eq => break :result .{ .overflow_bit = .zero_u1, .inst = lhs },
14929 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null),
14930 }
14931 },
14932 .vector => {
14933 var any_positive: bool = false;
14934 for (0..rhs_ty.vectorLen(zcu)) |elem_idx| {
14935 const rhs_elem = try rhs_val.elemValue(pt, elem_idx);
14936 if (rhs_elem.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, elem_idx);
14937 switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) {
14938 .gt => {
14939 var rhs_elem_space: Value.BigIntSpace = undefined;
14940 const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu);
14941 if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) {
14942 return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx);
14943 }
14944 any_positive = true;
14945 },
14946 .eq => {},
14947 .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx),
14948 }
14949 }
14950 if (!any_positive) break :result .{ .overflow_bit = try pt.aggregateSplatValue(overflow_ty, .zero_u1), .inst = lhs };
14951 },
14952 else => unreachable,
14953 }
14954 if (rhs_val.compareAllWithZero(.eq, zcu)) {
14955 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
14956 }
14957 } else {
14958 if (scalar_ty.toIntern() == .comptime_int_type) {
14959 return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{});
14960 }
14961 if (maybe_lhs_val) |lhs_val| {
14962 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
14963 if (lhs_val.compareAllWithZero(.eq, zcu)) {
14964 break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs };
14965 }
14966 }
14967 }
14968 },
14969 else => unreachable,
14970 }
14971
14972 const air_tag: Air.Inst.Tag = switch (zir_tag) {
14973 .add_with_overflow => .add_with_overflow,
14974 .mul_with_overflow => .mul_with_overflow,
14975 .sub_with_overflow => .sub_with_overflow,
14976 .shl_with_overflow => .shl_with_overflow,
14977 else => unreachable,
14978 };
14979
14980 return block.addInst(.{
14981 .tag = air_tag,
14982 .data = .{ .ty_pl = .{
14983 .ty = tuple_ty,
14984 .payload = try block.sema.addExtra(Air.Bin{
14985 .lhs = lhs,
14986 .rhs = rhs,
14987 }),
14988 } },
14989 });
14990 };
14991
14992 if (result.inst != .none) {
14993 if (sema.resolveValue(result.inst)) |some| {
14994 result.wrapped = some;
14995 result.inst = .none;
14996 }
14997 }
14998
14999 if (result.inst == .none) {
15000 return Air.internedToRef((try pt.aggregateValue(tuple_ty, &.{
15001 result.wrapped.toIntern(),
15002 result.overflow_bit.toIntern(),
15003 })).toIntern());
15004 }
15005
15006 const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2);
15007 element_refs[0] = result.inst;
15008 element_refs[1] = Air.internedToRef(result.overflow_bit.toIntern());
15009 return block.addAggregateInit(tuple_ty, element_refs);
15010}
15011
15012fn splat(sema: *Sema, ty: Type, val: Value) !Value {
15013 const pt = sema.pt;
15014 if (ty.zigTypeTag(pt.zcu) != .vector) return val;
15015 return pt.aggregateSplatValue(ty, val);
15016}
15017
15018fn analyzeArithmetic(
15019 sema: *Sema,
15020 block: *Block,
15021 zir_tag: Zir.Inst.Tag,
15022 lhs: Air.Inst.Ref,
15023 rhs: Air.Inst.Ref,
15024 src: LazySrcLoc,
15025 lhs_src: LazySrcLoc,
15026 rhs_src: LazySrcLoc,
15027 want_safety: bool,
15028) CompileError!Air.Inst.Ref {
15029 const pt = sema.pt;
15030 const zcu = pt.zcu;
15031 const lhs_ty = sema.typeOf(lhs);
15032 const rhs_ty = sema.typeOf(rhs);
15033 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
15034 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
15035 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
15036
15037 if (lhs_zig_ty_tag == .pointer) {
15038 if (rhs_zig_ty_tag == .pointer) {
15039 if (lhs_ty.ptrSize(zcu) != .slice and rhs_ty.ptrSize(zcu) != .slice) {
15040 if (zir_tag != .sub) {
15041 return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction");
15042 }
15043
15044 // TODO: these semantics are really weird. Pointer subtraction works in increments
15045 // of the pointer child for indexable pointers (excluding pointers to vectors),
15046 // which makes sense, but we also allow it for arbitrary single-item pointers, which
15047 // leads to the weird result that subtraction of '*T' works completely differently
15048 // depending on whether 'T' is an array. That seems dangerous and confusing, and
15049 // requires the odd logic below. This behavior originally came from a now-removed
15050 // function `Type.elemType2`, which was removed precisely *because* the thing it did
15051 // wasn't really well-defined; for that reason, these semantics were probably
15052 // largely accidental to begin with. We should change the langauge to avoid this
15053 // confusing behavior. For instance, perhaps pointer subtraction should only work on
15054 // indexable pointers.
15055 const lhs_elem_ty = ty: {
15056 const ptr_elem_ty = lhs_ty.childType(zcu);
15057 if (lhs_ty.ptrSize(zcu) == .one and ptr_elem_ty.zigTypeTag(zcu) == .array) break :ty ptr_elem_ty.childType(zcu);
15058 break :ty ptr_elem_ty;
15059 };
15060 const rhs_elem_ty = ty: {
15061 const ptr_elem_ty = rhs_ty.childType(zcu);
15062 if (rhs_ty.ptrSize(zcu) == .one and ptr_elem_ty.zigTypeTag(zcu) == .array) break :ty ptr_elem_ty.childType(zcu);
15063 break :ty ptr_elem_ty;
15064 };
15065 if (lhs_elem_ty.toIntern() != rhs_elem_ty.toIntern()) {
15066 return sema.fail(block, src, "incompatible pointer arithmetic operands '{f}' and '{f}'", .{
15067 lhs_ty.fmt(pt), rhs_ty.fmt(pt),
15068 });
15069 }
15070
15071 try sema.ensureLayoutResolved(lhs_elem_ty, src, .ptr_offset);
15072 const elem_size = lhs_elem_ty.abiSize(zcu);
15073 if (elem_size == 0) {
15074 return sema.fail(block, src, "pointer subtraction requires element type '{f}' to have runtime bits", .{
15075 lhs_elem_ty.fmt(pt),
15076 });
15077 }
15078
15079 const runtime_src = runtime_src: {
15080 if (sema.resolveValue(lhs)) |lhs_value| {
15081 if (sema.resolveValue(rhs)) |rhs_value| {
15082 const lhs_ptr = switch (zcu.intern_pool.indexToKey(lhs_value.toIntern())) {
15083 .undef => return sema.failWithUseOfUndef(block, lhs_src, null),
15084 .ptr => |ptr| ptr,
15085 else => unreachable,
15086 };
15087 const rhs_ptr = switch (zcu.intern_pool.indexToKey(rhs_value.toIntern())) {
15088 .undef => return sema.failWithUseOfUndef(block, rhs_src, null),
15089 .ptr => |ptr| ptr,
15090 else => unreachable,
15091 };
15092 // Make sure the pointers point to the same data.
15093 if (!lhs_ptr.base_addr.eql(rhs_ptr.base_addr)) break :runtime_src src;
15094 const address = std.math.sub(u64, lhs_ptr.byte_offset, rhs_ptr.byte_offset) catch
15095 return sema.fail(block, src, "operation results in overflow", .{});
15096 const result = address / elem_size;
15097 return try pt.intRef(.usize, result);
15098 } else {
15099 break :runtime_src lhs_src;
15100 }
15101 } else {
15102 break :runtime_src rhs_src;
15103 }
15104 };
15105
15106 try sema.requireRuntimeBlock(block, src, runtime_src);
15107 try sema.checkLogicalPtrOperation(block, src, lhs_ty);
15108 try sema.checkLogicalPtrOperation(block, src, rhs_ty);
15109 const lhs_int = try block.addTyOp(.int_from_ptr, .usize, lhs);
15110 const rhs_int = try block.addTyOp(.int_from_ptr, .usize, rhs);
15111 const address = try block.addBinOp(.sub_wrap, lhs_int, rhs_int);
15112 return try block.addBinOp(.div_exact, address, try pt.intRef(.usize, elem_size));
15113 }
15114 } else {
15115 switch (lhs_ty.ptrSize(zcu)) {
15116 .one, .slice => {},
15117 .many, .c => {
15118 const air_tag: Air.Inst.Tag = switch (zir_tag) {
15119 .add => .ptr_add,
15120 .sub => .ptr_sub,
15121 else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
15122 };
15123
15124 try sema.ensureLayoutResolved(lhs_ty.childType(zcu), src, .ptr_offset);
15125 return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, rhs_src);
15126 },
15127 }
15128 }
15129 }
15130
15131 const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{
15132 .override = &.{ lhs_src, rhs_src },
15133 });
15134
15135 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
15136 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
15137
15138 const scalar_type = resolved_type.scalarType(zcu);
15139 const scalar_tag = scalar_type.zigTypeTag(zcu);
15140
15141 try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag);
15142
15143 // The rules we'll implement below are as follows:
15144 //
15145 // * If both operands are comptime-known, we call the corresponding function in `arith` to get
15146 // the comptime-known result. Inputs which would be IB at runtime are compile errors.
15147 //
15148 // * Otherwise, if one operand is comptime-known `undefined`, we trigger a compile error if this
15149 // operator can ever possibly trigger IB, or otherwise return comptime-known `undefined`.
15150 //
15151 // * No other comptime operand detemines a comptime result; e.g. `0 * x` isn't always `0` because
15152 // of `undefined`. Therefore, the remaining cases all become runtime operations.
15153
15154 const is_int = switch (scalar_tag) {
15155 .int, .comptime_int => true,
15156 .float, .comptime_float => false,
15157 else => unreachable,
15158 };
15159
15160 const maybe_lhs_val = sema.resolveValue(casted_lhs);
15161 const maybe_rhs_val = sema.resolveValue(casted_rhs);
15162
15163 if (maybe_lhs_val) |lhs_val| {
15164 if (maybe_rhs_val) |rhs_val| {
15165 const result_val = switch (zir_tag) {
15166 .add, .add_unsafe => try arith.add(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src),
15167 .addwrap => try arith.addWrap(sema, resolved_type, lhs_val, rhs_val),
15168 .add_sat => try arith.addSat(sema, resolved_type, lhs_val, rhs_val),
15169 .sub => try arith.sub(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src),
15170 .subwrap => try arith.subWrap(sema, resolved_type, lhs_val, rhs_val),
15171 .sub_sat => try arith.subSat(sema, resolved_type, lhs_val, rhs_val),
15172 .mul => try arith.mul(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src),
15173 .mulwrap => try arith.mulWrap(sema, resolved_type, lhs_val, rhs_val),
15174 .mul_sat => try arith.mulSat(sema, resolved_type, lhs_val, rhs_val),
15175 else => unreachable,
15176 };
15177 return Air.internedToRef(result_val.toIntern());
15178 }
15179 }
15180
15181 const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag, const allow_undef: bool = switch (zir_tag) {
15182 .add, .add_unsafe => .{ if (block.float_mode == .optimized) .add_optimized else .add, .add_safe, !is_int },
15183 .addwrap => .{ .add_wrap, .add_wrap, true },
15184 .add_sat => .{ .add_sat, .add_sat, true },
15185 .sub => .{ if (block.float_mode == .optimized) .sub_optimized else .sub, .sub_safe, !is_int },
15186 .subwrap => .{ .sub_wrap, .sub_wrap, true },
15187 .sub_sat => .{ .sub_sat, .sub_sat, true },
15188 .mul => .{ if (block.float_mode == .optimized) .mul_optimized else .mul, .mul_safe, !is_int },
15189 .mulwrap => .{ .mul_wrap, .mul_wrap, true },
15190 .mul_sat => .{ .mul_sat, .mul_sat, true },
15191 else => unreachable,
15192 };
15193
15194 if (allow_undef) {
15195 if (maybe_lhs_val) |lhs_val| {
15196 if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
15197 }
15198 if (maybe_rhs_val) |rhs_val| {
15199 if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type);
15200 }
15201 } else {
15202 if (maybe_lhs_val) |lhs_val| {
15203 try sema.checkAllScalarsDefined(block, lhs_src, lhs_val);
15204 }
15205 if (maybe_rhs_val) |rhs_val| {
15206 try sema.checkAllScalarsDefined(block, rhs_src, rhs_val);
15207 }
15208 }
15209
15210 if (block.wantSafety() and want_safety and scalar_tag == .int) {
15211 if (air_tag != air_tag_safe) try sema.preparePanicId(src, .integer_overflow);
15212 return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs);
15213 }
15214 return block.addBinOp(air_tag, casted_lhs, casted_rhs);
15215}
15216
15217/// Asserts that the layout of the pointer child type is already resolved.
15218fn analyzePtrArithmetic(
15219 sema: *Sema,
15220 block: *Block,
15221 op_src: LazySrcLoc,
15222 ptr: Air.Inst.Ref,
15223 uncasted_offset: Air.Inst.Ref,
15224 air_tag: Air.Inst.Tag,
15225 offset_src: LazySrcLoc,
15226) CompileError!Air.Inst.Ref {
15227 // TODO if the operand is comptime-known to be negative, or is a negative int,
15228 // coerce to isize instead of usize.
15229 const offset = try sema.coerce(block, .usize, uncasted_offset, offset_src);
15230 const pt = sema.pt;
15231 const zcu = pt.zcu;
15232 const ptr_ty = sema.typeOf(ptr);
15233 const ptr_info = ptr_ty.ptrInfo(zcu);
15234 assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c);
15235
15236 const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, offset_src, offset)) |val| off: {
15237 break :off val.toUnsignedInt(zcu);
15238 } else null;
15239
15240 const elem_ty: Type = .fromInterned(ptr_info.child);
15241 elem_ty.assertHasLayout(zcu);
15242
15243 switch (elem_ty.classify(zcu)) {
15244 .no_possible_value, .one_possible_value => {
15245 // Offset will be multiplied by zero, so result is the same as the base pointer.
15246 return ptr;
15247 },
15248 else => {},
15249 }
15250
15251 const elem_ptr_ty = try ptr_ty.elemPtrType(maybe_index, pt);
15252 // `elem_ptr_ty` is a single-item pointer, but we want a many-item or C pointer, and to preserve
15253 // any input sentinel.
15254 const new_ptr_ty = try pt.ptrType(info: {
15255 var info = elem_ptr_ty.ptrInfo(zcu);
15256 info.flags.size = ptr_info.flags.size;
15257 info.sentinel = ptr_info.sentinel;
15258 break :info info;
15259 });
15260
15261 ct: {
15262 const ptr_val = sema.resolveValue(ptr) orelse break :ct;
15263 if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty);
15264 const index = maybe_index orelse break :ct;
15265
15266 if (index == 0) return ptr;
15267 if (air_tag == .ptr_sub) {
15268 const elem_size = elem_ty.abiSize(zcu);
15269 return .fromValue(try sema.ptrSubtract(block, op_src, ptr_val, index * elem_size, new_ptr_ty));
15270 } else {
15271 return .fromValue(try pt.getCoerced(try ptr_val.ptrElem(index, pt), new_ptr_ty));
15272 }
15273 }
15274
15275 try sema.checkLogicalPtrOperation(block, op_src, ptr_ty);
15276
15277 return block.addInst(.{
15278 .tag = air_tag,
15279 .data = .{ .ty_pl = .{
15280 .ty = new_ptr_ty,
15281 .payload = try sema.addExtra(Air.Bin{
15282 .lhs = ptr,
15283 .rhs = offset,
15284 }),
15285 } },
15286 });
15287}
15288
15289fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
15290 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
15291 const src = block.nodeOffset(inst_data.src_node);
15292 const ptr_src = src; // TODO better source location
15293 const ptr = sema.resolveInst(inst_data.operand);
15294 return sema.analyzeLoad(block, src, ptr, ptr_src);
15295}
15296
15297fn zirAsm(
15298 sema: *Sema,
15299 block: *Block,
15300 extended: Zir.Inst.Extended.InstData,
15301 tmpl_is_expr: bool,
15302) CompileError!Air.Inst.Ref {
15303 const pt = sema.pt;
15304 const zcu = pt.zcu;
15305 const comp = zcu.comp;
15306 const gpa = comp.gpa;
15307 const io = comp.io;
15308 const ip = &zcu.intern_pool;
15309
15310 const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand);
15311 const src = block.nodeOffset(extra.data.src_node);
15312 const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node });
15313 const small: Zir.Inst.Asm.Small = @bitCast(extended.small);
15314 const outputs_len = small.outputs_len;
15315 const inputs_len = small.inputs_len;
15316 const is_volatile = small.is_volatile;
15317 const is_global_assembly = sema.func_index == .none;
15318
15319 const asm_source: []const u8 = if (tmpl_is_expr) s: {
15320 const tmpl: Zir.Inst.Ref = @fromBackingInt(@intCast(@backingInt(extra.data.asm_source)));
15321 break :s try sema.resolveConstString(block, src, tmpl, .{ .simple = .inline_assembly_code });
15322 } else sema.code.nullTerminatedString(extra.data.asm_source);
15323
15324 if (is_global_assembly) {
15325 assert(outputs_len == 0); // validated by AstGen
15326 assert(inputs_len == 0); // validated by AstGen
15327 assert(extra.data.clobbers == .none); // validated by AstGen
15328 assert(!is_volatile); // validated by AstGen
15329
15330 try zcu.addGlobalAssembly(sema.owner, asm_source);
15331 return .void_value;
15332 }
15333
15334 try sema.requireRuntimeBlock(block, src, null);
15335
15336 var extra_i = extra.end;
15337 var output_type_bits = extra.data.output_type_bits;
15338 var needed_capacity: usize = @typeInfo(Air.Asm).@"struct".field_names.len + outputs_len + inputs_len;
15339
15340 const ConstraintName = struct { c: []const u8, n: []const u8 };
15341 const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len);
15342 const outputs = try sema.arena.alloc(ConstraintName, outputs_len);
15343 var expr_ty: Type = .void;
15344
15345 for (out_args, 0..) |*arg, out_i| {
15346 const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i);
15347 const output_src = block.src(.{ .asm_output = .{
15348 .offset = src.offset.node_offset.x,
15349 .output_index = @intCast(out_i),
15350 } });
15351 extra_i = output.end;
15352
15353 const is_type = @as(u1, @truncate(output_type_bits)) != 0;
15354 output_type_bits >>= 1;
15355
15356 const name = sema.code.nullTerminatedString(output.data.name);
15357
15358 const out_ty: Type = out_ty: {
15359 if (is_type) {
15360 // Indicate the output is the asm instruction return value.
15361 arg.* = .none;
15362
15363 const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand);
15364 try sema.ensureLayoutResolved(out_ty, ret_ty_src, .asm_out_type);
15365 expr_ty = out_ty;
15366 break :out_ty out_ty;
15367 } else {
15368 const inst = sema.resolveInst(output.data.operand);
15369 arg.* = inst;
15370
15371 if (!sema.checkRuntimeValue(inst)) {
15372 const output_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
15373 return sema.failWithContainsReferenceToComptimeVar(block, output_src, output_name, "assembly output", .fromInterned(inst.toInterned().?));
15374 }
15375 break :out_ty sema.typeOf(inst).childType(zcu);
15376 }
15377 };
15378 switch (out_ty.zigTypeTag(zcu)) {
15379 .int, .float, .bool, .vector => {},
15380
15381 .pointer => if (out_ty.isSlice(zcu)) return sema.failWithOwnedErrorMsg(block, msg: {
15382 const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)});
15383 errdefer msg.destroy(gpa);
15384 try sema.errNote(output_src, msg, "consider separate outputs for 'ptr' and 'len'", .{});
15385 break :msg msg;
15386 }),
15387
15388 .optional => if (!out_ty.isPtrLikeOptional(zcu)) {
15389 return sema.fail(block, output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)});
15390 },
15391
15392 .@"enum" => switch (ip.loadEnumType(out_ty.toIntern()).int_tag_mode) {
15393 .explicit => {},
15394 .auto => return sema.failWithOwnedErrorMsg(block, msg: {
15395 const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)});
15396 errdefer msg.destroy(gpa);
15397 try sema.errNote(out_ty.srcLoc(zcu), msg, "integer tag type of enum is inferred", .{});
15398 try sema.errNote(out_ty.srcLoc(zcu), msg, "consider explicitly specifying the integer tag type", .{});
15399 break :msg msg;
15400 }),
15401 },
15402
15403 .@"struct" => switch (out_ty.containerLayout(zcu)) {
15404 .@"packed" => {},
15405 .auto, .@"extern" => return sema.failWithOwnedErrorMsg(block, msg: {
15406 const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)});
15407 errdefer msg.destroy(gpa);
15408 try sema.errNote(output_src, msg, "struct types cannot be passed to inline assembly", .{});
15409 try sema.addDeclaredHereNote(msg, out_ty);
15410 break :msg msg;
15411 }),
15412 },
15413
15414 .@"union" => switch (out_ty.containerLayout(zcu)) {
15415 .@"packed" => {},
15416 .auto, .@"extern" => return sema.failWithOwnedErrorMsg(block, msg: {
15417 const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)});
15418 errdefer msg.destroy(gpa);
15419 try sema.errNote(output_src, msg, "union types cannot be passed to inline assembly", .{});
15420 try sema.addDeclaredHereNote(msg, out_ty);
15421 break :msg msg;
15422 }),
15423 },
15424
15425 .array => return sema.failWithOwnedErrorMsg(block, msg: {
15426 const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)});
15427 errdefer msg.destroy(gpa);
15428 try sema.errNote(output_src, msg, "array types cannot be passed to inline assembly", .{});
15429 break :msg msg;
15430 }),
15431
15432 .void,
15433 .type,
15434 .noreturn,
15435 .comptime_float,
15436 .comptime_int,
15437 .undefined,
15438 .null,
15439 .error_union,
15440 .error_set,
15441 .@"fn",
15442 .@"opaque",
15443 .frame,
15444 .@"anyframe",
15445 .enum_literal,
15446 .spirv,
15447 => return sema.fail(block, output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)}),
15448 }
15449
15450 const constraint = sema.code.nullTerminatedString(output.data.constraint);
15451 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
15452
15453 // AstGen gives us a reference to a variable
15454 if (arg.* != .none and sema.typeOf(arg.*).isConstPtr(zcu)) {
15455 return sema.fail(block, output_src, "asm cannot output to const '{s}'", .{name});
15456 }
15457
15458 outputs[out_i] = .{ .c = constraint, .n = name };
15459 }
15460
15461 const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len);
15462 const inputs = try sema.arena.alloc(ConstraintName, inputs_len);
15463
15464 for (args, 0..) |*arg, arg_i| {
15465 const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i);
15466 const input_src = block.src(.{ .asm_input = .{
15467 .offset = src.offset.node_offset.x,
15468 .input_index = @intCast(arg_i),
15469 } });
15470 extra_i = input.end;
15471
15472 const uncasted_arg = sema.resolveInst(input.data.operand);
15473 const name = sema.code.nullTerminatedString(input.data.name);
15474 if (!sema.checkRuntimeValue(uncasted_arg)) {
15475 const input_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
15476 return sema.failWithContainsReferenceToComptimeVar(block, input_src, input_name, "assembly input", .fromInterned(uncasted_arg.toInterned().?));
15477 }
15478 const uncasted_arg_ty = sema.typeOf(uncasted_arg);
15479 switch (uncasted_arg_ty.zigTypeTag(zcu)) {
15480 .comptime_int => arg.* = try sema.coerce(block, .usize, uncasted_arg, src),
15481 .comptime_float => arg.* = try sema.coerce(block, .f64, uncasted_arg, src),
15482 else => {
15483 arg.* = uncasted_arg;
15484 },
15485 }
15486
15487 const constraint = sema.code.nullTerminatedString(input.data.constraint);
15488 if (zcu.getTarget().cpu.arch.isSpirV() and std.mem.eql(u8, constraint, "c")) {
15489 const val = sema.resolveValue(arg.*) orelse {
15490 return sema.fail(block, input_src, "assembly input with 'c' constraint must be compile-time known", .{});
15491 };
15492 if (val.isUndef(zcu)) {
15493 return sema.fail(block, input_src, "assembly input with 'c' constraint cannot be undefined", .{});
15494 }
15495 const bad_type: bool = switch (uncasted_arg_ty.zigTypeTag(zcu)) {
15496 .bool, .int, .float, .comptime_int, .comptime_float, .enum_literal => false,
15497 .vector => switch (uncasted_arg_ty.childType(zcu).zigTypeTag(zcu)) {
15498 .bool, .int, .float => false,
15499 else => true,
15500 },
15501 else => true,
15502 };
15503 if (bad_type) return sema.fail(block, input_src, "unsupported type '{f}' for 'c' constraint", .{uncasted_arg_ty.fmt(pt)});
15504 }
15505 needed_capacity += (constraint.len + name.len + (2 + 3)) / 4;
15506 inputs[arg_i] = .{ .c = constraint, .n = name };
15507 }
15508
15509 const clobbers_src = block.src(.{ .asm_clobbers = src.offset.node_offset.x });
15510 const clobbers_ty = try sema.getStdLangType(src, .@"assembly.Clobbers");
15511 const clobbers = if (extra.data.clobbers == .none) empty: {
15512 break :empty try sema.structInitEmpty(block, clobbers_ty, src, src);
15513 } else clobbers: {
15514 const uncoerced = sema.resolveInst(extra.data.clobbers);
15515 break :clobbers try sema.coerce(block, clobbers_ty, uncoerced, clobbers_src);
15516 };
15517 const clobbers_val = try sema.resolveConstDefinedValue(block, clobbers_src, clobbers, .{ .simple = .clobber });
15518 needed_capacity += asm_source.len / 4 + 1;
15519
15520 try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity);
15521 const asm_air = try block.addInst(.{
15522 .tag = .assembly,
15523 .data = .{ .ty_pl = .{
15524 .ty = expr_ty,
15525 .payload = sema.addExtraAssumeCapacity(Air.Asm{
15526 .source_len = @intCast(asm_source.len),
15527 .inputs_len = @intCast(args.len),
15528 .clobbers = clobbers_val.toIntern(),
15529 .flags = .{
15530 .is_volatile = is_volatile,
15531 .outputs_len = outputs_len,
15532 },
15533 }),
15534 } },
15535 });
15536 sema.appendRefsAssumeCapacity(out_args);
15537 sema.appendRefsAssumeCapacity(args);
15538 {
15539 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
15540 @memcpy(buffer[0..asm_source.len], asm_source);
15541 buffer[asm_source.len] = 0;
15542 sema.air_extra.items.len += asm_source.len / 4 + 1;
15543 }
15544 for (outputs) |o| {
15545 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
15546 @memcpy(buffer[0..o.c.len], o.c);
15547 buffer[o.c.len] = 0;
15548 @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n);
15549 buffer[o.c.len + 1 + o.n.len] = 0;
15550 sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4;
15551 }
15552 for (inputs) |input| {
15553 const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice());
15554 @memcpy(buffer[0..input.c.len], input.c);
15555 buffer[input.c.len] = 0;
15556 @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n);
15557 buffer[input.c.len + 1 + input.n.len] = 0;
15558 sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4;
15559 }
15560 if (try expr_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
15561 return asm_air;
15562}
15563
15564/// Only called for equality operators. See also `zirCmp`.
15565fn zirCmpEq(
15566 sema: *Sema,
15567 block: *Block,
15568 inst: Zir.Inst.Index,
15569 op: std.math.CompareOperator,
15570 air_tag: Air.Inst.Tag,
15571) CompileError!Air.Inst.Ref {
15572 const pt = sema.pt;
15573 const zcu = pt.zcu;
15574 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
15575 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
15576 const src: LazySrcLoc = block.nodeOffset(inst_data.src_node);
15577 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
15578 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
15579 const lhs = sema.resolveInst(extra.lhs);
15580 const rhs = sema.resolveInst(extra.rhs);
15581
15582 const lhs_ty = sema.typeOf(lhs);
15583 const rhs_ty = sema.typeOf(rhs);
15584 const lhs_ty_tag = lhs_ty.zigTypeTag(zcu);
15585 const rhs_ty_tag = rhs_ty.zigTypeTag(zcu);
15586 if (lhs_ty_tag == .null and rhs_ty_tag == .null) {
15587 // null == null, null != null
15588 return if (op == .eq) .bool_true else .bool_false;
15589 }
15590
15591 // comparing null with optionals
15592 if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) {
15593 return sema.analyzeIsNull(block, src, rhs, op == .neq);
15594 }
15595 if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) {
15596 return sema.analyzeIsNull(block, src, lhs, op == .neq);
15597 }
15598
15599 if (lhs_ty_tag == .null or rhs_ty_tag == .null) {
15600 const non_null_type = if (lhs_ty_tag == .null) rhs_ty else lhs_ty;
15601 return sema.fail(block, src, "comparison of '{f}' with null", .{non_null_type.fmt(pt)});
15602 }
15603
15604 if (lhs_ty_tag == .@"union" and (rhs_ty_tag == .enum_literal or rhs_ty_tag == .@"enum")) {
15605 return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op);
15606 }
15607 if (rhs_ty_tag == .@"union" and (lhs_ty_tag == .enum_literal or lhs_ty_tag == .@"enum")) {
15608 return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op);
15609 }
15610
15611 if (lhs_ty_tag == .error_set and rhs_ty_tag == .error_set) {
15612 const runtime_src: LazySrcLoc = src: {
15613 if (sema.resolveValue(lhs)) |lval| {
15614 if (sema.resolveValue(rhs)) |rval| {
15615 if (lval.isUndef(zcu) or rval.isUndef(zcu)) return .undef_bool;
15616 const lkey = zcu.intern_pool.indexToKey(lval.toIntern());
15617 const rkey = zcu.intern_pool.indexToKey(rval.toIntern());
15618 return if ((lkey.err.name == rkey.err.name) == (op == .eq))
15619 .bool_true
15620 else
15621 .bool_false;
15622 } else {
15623 break :src rhs_src;
15624 }
15625 } else {
15626 break :src lhs_src;
15627 }
15628 };
15629 try sema.requireRuntimeBlock(block, src, runtime_src);
15630 return block.addBinOp(air_tag, lhs, rhs);
15631 }
15632 if (lhs_ty_tag == .type and rhs_ty_tag == .type) {
15633 const lhs_as_type = try sema.analyzeAsType(block, lhs_src, .type, lhs);
15634 const rhs_as_type = try sema.analyzeAsType(block, rhs_src, .type, rhs);
15635 return if (lhs_as_type.eql(rhs_as_type) == (op == .eq)) .bool_true else .bool_false;
15636 }
15637 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true);
15638}
15639
15640fn analyzeCmpUnionTag(
15641 sema: *Sema,
15642 block: *Block,
15643 src: LazySrcLoc,
15644 un: Air.Inst.Ref,
15645 un_src: LazySrcLoc,
15646 tag: Air.Inst.Ref,
15647 tag_src: LazySrcLoc,
15648 op: std.math.CompareOperator,
15649) CompileError!Air.Inst.Ref {
15650 const pt = sema.pt;
15651 const zcu = pt.zcu;
15652 const union_ty = sema.typeOf(un);
15653 const union_tag_ty = union_ty.unionTagType(zcu) orelse {
15654 const msg = msg: {
15655 const msg = try sema.errMsg(un_src, "comparison of union and enum literal is only valid for tagged union types", .{});
15656 errdefer msg.destroy(sema.gpa);
15657 try sema.errNote(union_ty.srcLoc(zcu), msg, "union '{f}' is not a tagged union", .{union_ty.fmt(pt)});
15658 break :msg msg;
15659 };
15660 return sema.failWithOwnedErrorMsg(block, msg);
15661 };
15662 // Coerce both the union and the tag to the union's tag type, and then execute the
15663 // enum comparison codepath.
15664 const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src);
15665 const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src);
15666
15667 if (sema.resolveValue(coerced_tag)) |enum_val| {
15668 if (enum_val.isUndef(zcu)) return .undef_bool;
15669 const field_ty = union_ty.unionFieldType(enum_val, zcu).?;
15670 if (field_ty.classify(zcu) == .no_possible_value) {
15671 return .bool_false;
15672 }
15673 }
15674
15675 return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src);
15676}
15677
15678/// Only called for non-equality operators. See also `zirCmpEq`.
15679fn zirCmp(
15680 sema: *Sema,
15681 block: *Block,
15682 inst: Zir.Inst.Index,
15683 op: std.math.CompareOperator,
15684) CompileError!Air.Inst.Ref {
15685 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
15686 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
15687 const src: LazySrcLoc = block.nodeOffset(inst_data.src_node);
15688 const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
15689 const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
15690 const lhs = sema.resolveInst(extra.lhs);
15691 const rhs = sema.resolveInst(extra.rhs);
15692 return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false);
15693}
15694
15695fn analyzeCmp(
15696 sema: *Sema,
15697 block: *Block,
15698 src: LazySrcLoc,
15699 lhs: Air.Inst.Ref,
15700 rhs: Air.Inst.Ref,
15701 op: std.math.CompareOperator,
15702 lhs_src: LazySrcLoc,
15703 rhs_src: LazySrcLoc,
15704 is_equality_cmp: bool,
15705) CompileError!Air.Inst.Ref {
15706 const pt = sema.pt;
15707 const zcu = pt.zcu;
15708 const lhs_ty = sema.typeOf(lhs);
15709 const rhs_ty = sema.typeOf(rhs);
15710 if (lhs_ty.zigTypeTag(zcu) != .optional and rhs_ty.zigTypeTag(zcu) != .optional) {
15711 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
15712 }
15713
15714 if (lhs_ty.zigTypeTag(zcu) == .vector and rhs_ty.zigTypeTag(zcu) == .vector) {
15715 return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src);
15716 }
15717 if (lhs_ty.isNumeric(zcu) and rhs_ty.isNumeric(zcu)) {
15718 // This operation allows any combination of integer and float types, regardless of the
15719 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
15720 // numeric types.
15721 return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src);
15722 }
15723 if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_union and rhs_ty.zigTypeTag(zcu) == .error_set) {
15724 if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| {
15725 if (lhs_val.errorUnionIsPayload(zcu)) return .bool_false;
15726 }
15727 const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs);
15728 return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src);
15729 }
15730 if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_set and rhs_ty.zigTypeTag(zcu) == .error_union) {
15731 if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| {
15732 if (rhs_val.errorUnionIsPayload(zcu)) return .bool_false;
15733 }
15734 const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs);
15735 return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src);
15736 }
15737 const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
15738 const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } });
15739 if (!resolved_type.isSelfComparable(zcu, is_equality_cmp)) {
15740 return sema.fail(block, src, "operator {s} not allowed for type '{f}'", .{
15741 compareOperatorName(op), resolved_type.fmt(pt),
15742 });
15743 }
15744 const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
15745 const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
15746 return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src);
15747}
15748
15749fn compareOperatorName(comp: std.math.CompareOperator) []const u8 {
15750 return switch (comp) {
15751 .lt => "<",
15752 .lte => "<=",
15753 .eq => "==",
15754 .gte => ">=",
15755 .gt => ">",
15756 .neq => "!=",
15757 };
15758}
15759
15760fn cmpSelf(
15761 sema: *Sema,
15762 block: *Block,
15763 src: LazySrcLoc,
15764 casted_lhs: Air.Inst.Ref,
15765 casted_rhs: Air.Inst.Ref,
15766 op: std.math.CompareOperator,
15767 lhs_src: LazySrcLoc,
15768 rhs_src: LazySrcLoc,
15769) CompileError!Air.Inst.Ref {
15770 const pt = sema.pt;
15771 const zcu = pt.zcu;
15772 const resolved_type = sema.typeOf(casted_lhs);
15773
15774 const maybe_lhs_val = sema.resolveValue(casted_lhs);
15775 const maybe_rhs_val = sema.resolveValue(casted_rhs);
15776 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
15777 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
15778
15779 const runtime_src: LazySrcLoc = src: {
15780 if (maybe_lhs_val) |lhs_val| {
15781 if (maybe_rhs_val) |rhs_val| {
15782 if (resolved_type.zigTypeTag(zcu) == .vector) {
15783 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type);
15784 return Air.internedToRef(cmp_val.toIntern());
15785 }
15786
15787 return if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type))
15788 .bool_true
15789 else
15790 .bool_false;
15791 } else {
15792 if (resolved_type.zigTypeTag(zcu) == .bool) {
15793 // We can lower bool eq/neq more efficiently.
15794 return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src);
15795 }
15796 break :src rhs_src;
15797 }
15798 } else {
15799 // For bools, we still check the other operand, because we can lower
15800 // bool eq/neq more efficiently.
15801 if (resolved_type.zigTypeTag(zcu) == .bool) {
15802 if (maybe_rhs_val) |rhs_val| {
15803 return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src);
15804 }
15805 }
15806 break :src lhs_src;
15807 }
15808 };
15809 try sema.requireRuntimeBlock(block, src, runtime_src);
15810 if (resolved_type.zigTypeTag(zcu) == .vector) {
15811 return block.addCmpVector(casted_lhs, casted_rhs, op);
15812 }
15813 const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized);
15814 return block.addBinOp(tag, casted_lhs, casted_rhs);
15815}
15816
15817/// cmp_eq (x, false) => not(x)
15818/// cmp_eq (x, true ) => x
15819/// cmp_neq(x, false) => x
15820/// cmp_neq(x, true ) => not(x)
15821fn runtimeBoolCmp(
15822 sema: *Sema,
15823 block: *Block,
15824 src: LazySrcLoc,
15825 op: std.math.CompareOperator,
15826 lhs: Air.Inst.Ref,
15827 rhs: bool,
15828 runtime_src: LazySrcLoc,
15829) CompileError!Air.Inst.Ref {
15830 if ((op == .neq) == rhs) {
15831 try sema.requireRuntimeBlock(block, src, runtime_src);
15832 return block.addTyOp(.not, .bool, lhs);
15833 } else {
15834 return lhs;
15835 }
15836}
15837
15838fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
15839 const pt = sema.pt;
15840 const zcu = pt.zcu;
15841 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
15842 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
15843 const ty = try sema.resolveType(block, operand_src, inst_data.operand);
15844 try sema.ensureLayoutResolved(ty, operand_src, .size_of);
15845 switch (ty.classify(zcu)) {
15846 .no_possible_value,
15847 => return sema.fail(block, operand_src, "no size available for uninstantiable type '{f}'", .{ty.fmt(pt)}),
15848
15849 .partially_comptime,
15850 .fully_comptime,
15851 => return sema.fail(block, operand_src, "no size available for comptime-only type '{f}'", .{ty.fmt(pt)}),
15852
15853 .one_possible_value => {
15854 assert(ty.abiSize(zcu) == 0);
15855 return .zero;
15856 },
15857
15858 .runtime => return .fromValue(try pt.intValue(.comptime_int, ty.abiSize(zcu))),
15859 }
15860}
15861
15862fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
15863 const pt = sema.pt;
15864 const zcu = pt.zcu;
15865 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
15866 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
15867 const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
15868 if (!operand_ty.hasBitRepresentation(zcu) and
15869 // TODO: allow these types too for now because this is used in some places. We need to
15870 // figure out whether we think errors and auto-enums have bit representations!
15871 operand_ty.zigTypeTag(zcu) != .error_set and
15872 operand_ty.zigTypeTag(zcu) != .@"enum")
15873 {
15874 return sema.fail(block, operand_src, "no bit size available for type '{f}'", .{operand_ty.fmt(pt)});
15875 }
15876 try sema.ensureLayoutResolved(operand_ty, operand_src, .size_of);
15877 return .fromValue(try pt.intValue(.comptime_int, operand_ty.bitSize(zcu)));
15878}
15879
15880fn zirThis(
15881 sema: *Sema,
15882 block: *Block,
15883 extended: Zir.Inst.Extended.InstData,
15884) CompileError!Air.Inst.Ref {
15885 _ = extended;
15886 return .fromIntern(sema.pt.zcu.namespacePtr(block.namespace).owner_type);
15887}
15888
15889fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
15890 const pt = sema.pt;
15891 const zcu = pt.zcu;
15892 const ip = &zcu.intern_pool;
15893 const captures = Type.fromInterned(zcu.namespacePtr(block.namespace).owner_type).getCaptures(zcu);
15894
15895 const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand))));
15896 const src = block.nodeOffset(src_node);
15897
15898 const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) {
15899 .@"comptime" => |index| return Air.internedToRef(index),
15900 .runtime => |index| index,
15901 .nav_val => |nav| return sema.analyzeNavVal(block, src, nav),
15902 .nav_ref => |nav| return sema.analyzeNavRef(block, src, nav),
15903 };
15904
15905 // The comptime case is handled already above. Runtime case below.
15906
15907 if (!block.is_typeof and sema.func_index == .none) {
15908 const msg = msg: {
15909 const name = name: {
15910 // TODO: we should probably store this name in the ZIR to avoid this complexity.
15911 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
15912 const tree = file.getTree(zcu) catch |err| {
15913 // In this case we emit a warning + a less precise source location.
15914 log.warn("unable to load {f}: {s}", .{
15915 file.path.fmt(zcu.comp), @errorName(err),
15916 });
15917 break :name null;
15918 };
15919 const node = src_node.toAbsolute(src_base_node);
15920 const token = tree.nodeMainToken(node);
15921 break :name tree.tokenSlice(token);
15922 };
15923
15924 const msg = if (name) |some|
15925 try sema.errMsg(src, "'{s}' not accessible outside function scope", .{some})
15926 else
15927 try sema.errMsg(src, "variable not accessible outside function scope", .{});
15928 errdefer msg.destroy(sema.gpa);
15929
15930 // TODO add "declared here" note
15931 break :msg msg;
15932 };
15933 return sema.failWithOwnedErrorMsg(block, msg);
15934 }
15935
15936 if (!block.is_typeof and !block.isComptime() and sema.func_index != .none) {
15937 const msg = msg: {
15938 const name = name: {
15939 const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?;
15940 const tree = file.getTree(zcu) catch |err| {
15941 // In this case we emit a warning + a less precise source location.
15942 log.warn("unable to load {f}: {s}", .{
15943 file.path.fmt(zcu.comp), @errorName(err),
15944 });
15945 break :name null;
15946 };
15947 const node = src_node.toAbsolute(src_base_node);
15948 const token = tree.nodeMainToken(node);
15949 break :name tree.tokenSlice(token);
15950 };
15951
15952 const msg = if (name) |some|
15953 try sema.errMsg(src, "'{s}' not accessible from inner function", .{some})
15954 else
15955 try sema.errMsg(src, "variable not accessible from inner function", .{});
15956 errdefer msg.destroy(sema.gpa);
15957
15958 try sema.errNote(block.nodeOffset(.zero), msg, "crossed function definition here", .{});
15959
15960 // TODO add "declared here" note
15961 break :msg msg;
15962 };
15963 return sema.failWithOwnedErrorMsg(block, msg);
15964 }
15965
15966 assert(block.is_typeof);
15967 // We need a dummy runtime instruction with the correct type.
15968 return block.addTy(.alloc, .fromInterned(capture_ty));
15969}
15970
15971fn zirRetAddr(
15972 sema: *Sema,
15973 block: *Block,
15974 extended: Zir.Inst.Extended.InstData,
15975) CompileError!Air.Inst.Ref {
15976 _ = sema;
15977 _ = extended;
15978 if (block.isComptime()) {
15979 // TODO: we could give a meaningful value here. #14938
15980 return .zero_usize;
15981 } else {
15982 return block.addNoOp(.ret_addr);
15983 }
15984}
15985
15986fn zirFrameAddress(
15987 sema: *Sema,
15988 block: *Block,
15989 extended: Zir.Inst.Extended.InstData,
15990) CompileError!Air.Inst.Ref {
15991 const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand))));
15992 const src = block.nodeOffset(src_node);
15993 try sema.requireRuntimeBlock(block, src, null);
15994 return try block.addNoOp(.frame_addr);
15995}
15996
15997fn zirBuiltinSrc(
15998 sema: *Sema,
15999 block: *Block,
16000 extended: Zir.Inst.Extended.InstData,
16001) CompileError!Air.Inst.Ref {
16002 const pt = sema.pt;
16003 const zcu = pt.zcu;
16004 const comp = zcu.comp;
16005 const gpa = comp.gpa;
16006 const io = comp.io;
16007 const ip = &zcu.intern_pool;
16008
16009 const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data;
16010 const fn_name = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).name;
16011 const file_scope = block.getFileScope(zcu);
16012
16013 const func_name_val = v: {
16014 const func_name_len = fn_name.length(ip);
16015 const array_ty = try pt.intern(.{ .array_type = .{
16016 .len = func_name_len,
16017 .sentinel = .zero_u8,
16018 .child = .u8_type,
16019 } });
16020 break :v try pt.intern(.{ .slice = .{
16021 .ty = .slice_const_u8_sentinel_0_type,
16022 .ptr = try pt.intern(.{ .ptr = .{
16023 .ty = .manyptr_const_u8_sentinel_0_type,
16024 .base_addr = .{ .uav = .{
16025 .orig_ty = .slice_const_u8_sentinel_0_type,
16026 .val = try pt.intern(.{ .aggregate = .{
16027 .ty = array_ty,
16028 .storage = .{ .bytes = fn_name.toString() },
16029 } }),
16030 } },
16031 .byte_offset = 0,
16032 } }),
16033 .len = (try pt.intValue(.usize, func_name_len)).toIntern(),
16034 } });
16035 };
16036
16037 const module_name_val = v: {
16038 const module_name = file_scope.mod.?.fully_qualified_name;
16039 const array_ty = try pt.intern(.{ .array_type = .{
16040 .len = module_name.len,
16041 .sentinel = .zero_u8,
16042 .child = .u8_type,
16043 } });
16044 break :v try pt.intern(.{ .slice = .{
16045 .ty = .slice_const_u8_sentinel_0_type,
16046 .ptr = try pt.intern(.{ .ptr = .{
16047 .ty = .manyptr_const_u8_sentinel_0_type,
16048 .base_addr = .{ .uav = .{
16049 .orig_ty = .slice_const_u8_sentinel_0_type,
16050 .val = try pt.intern(.{ .aggregate = .{
16051 .ty = array_ty,
16052 .storage = .{
16053 .bytes = try ip.getOrPutString(gpa, io, pt.tid, module_name, .maybe_embedded_nulls),
16054 },
16055 } }),
16056 } },
16057 .byte_offset = 0,
16058 } }),
16059 .len = (try pt.intValue(.usize, module_name.len)).toIntern(),
16060 } });
16061 };
16062
16063 const file_name_val = v: {
16064 const file_name = file_scope.sub_file_path;
16065 const array_ty = try pt.intern(.{ .array_type = .{
16066 .len = file_name.len,
16067 .sentinel = .zero_u8,
16068 .child = .u8_type,
16069 } });
16070 break :v try pt.intern(.{ .slice = .{
16071 .ty = .slice_const_u8_sentinel_0_type,
16072 .ptr = try pt.intern(.{ .ptr = .{
16073 .ty = .manyptr_const_u8_sentinel_0_type,
16074 .base_addr = .{ .uav = .{
16075 .orig_ty = .slice_const_u8_sentinel_0_type,
16076 .val = try pt.intern(.{ .aggregate = .{
16077 .ty = array_ty,
16078 .storage = .{
16079 .bytes = try ip.getOrPutString(gpa, io, pt.tid, file_name, .maybe_embedded_nulls),
16080 },
16081 } }),
16082 } },
16083 .byte_offset = 0,
16084 } }),
16085 .len = (try pt.intValue(.usize, file_name.len)).toIntern(),
16086 } });
16087 };
16088
16089 const src_loc_ty = try sema.getStdLangType(block.nodeOffset(.zero), .SourceLocation);
16090 const fields = .{
16091 // module: [:0]const u8,
16092 module_name_val,
16093 // file: [:0]const u8,
16094 file_name_val,
16095 // fn_name: [:0]const u8,
16096 func_name_val,
16097 // line: u32,
16098 (try pt.intValue(.u32, extra.line + 1)).toIntern(),
16099 // column: u32,
16100 (try pt.intValue(.u32, extra.column + 1)).toIntern(),
16101 };
16102 return Air.internedToRef((try pt.aggregateValue(src_loc_ty, &fields)).toIntern());
16103}
16104
16105fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
16106 const pt = sema.pt;
16107 const zcu = pt.zcu;
16108 const comp = zcu.comp;
16109 const gpa = comp.gpa;
16110 const io = comp.io;
16111 const ip = &zcu.intern_pool;
16112
16113 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
16114 const src = block.nodeOffset(inst_data.src_node);
16115 const ty = try sema.resolveType(block, src, inst_data.operand);
16116 const type_info_ty = try sema.getStdLangType(src, .Type);
16117 const type_info_tag_ty = type_info_ty.unionTagType(zcu).?;
16118
16119 try sema.ensureLayoutResolved(ty, src, .type_info);
16120
16121 if (ty.typeDeclInst(zcu)) |type_decl_inst| {
16122 try sema.declareDependency(.{ .namespace = type_decl_inst });
16123 }
16124
16125 switch (ty.zigTypeTag(zcu)) {
16126 .type,
16127 .void,
16128 .bool,
16129 .noreturn,
16130 .comptime_float,
16131 .comptime_int,
16132 .undefined,
16133 .null,
16134 .enum_literal,
16135 => |type_info_tag| return .fromValue(try pt.unionValue(
16136 type_info_ty,
16137 Value.uninterpret(type_info_tag, type_info_tag_ty, pt) catch |err| switch (err) {
16138 error.TypeMismatch => @panic("std.lang is corrupt"),
16139 error.OutOfMemory => |e| return e,
16140 },
16141 .void,
16142 )),
16143
16144 .@"fn" => {
16145 const fn_info_ty = try sema.getStdLangType(src, .@"Type.Fn");
16146 const param_attrs_ty = try sema.getStdLangType(src, .@"Type.Fn.ParamAttributes");
16147 const fn_attr_ty = try sema.getStdLangType(src, .@"Type.Fn.Attributes");
16148
16149 const func_ty_info = zcu.typeToFunc(ty).?;
16150 const param_type_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);
16151 const param_attr_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len);
16152 var func_is_generic = false;
16153
16154 for (
16155 param_type_vals,
16156 param_attr_vals,
16157 0..,
16158 ) |*param_type_val, *param_attr_val, param_index| {
16159 const param_ty = func_ty_info.param_types.get(ip)[param_index];
16160 const is_generic = param_ty == .generic_poison_type;
16161 const is_noalias, const is_comptime = flags: {
16162 const i = std.math.cast(u5, param_index) orelse break :flags .{ false, false };
16163 break :flags .{ func_ty_info.paramIsNoalias(i), func_ty_info.paramIsComptime(i) };
16164 };
16165
16166 if (is_generic or is_comptime or Type.fromInterned(param_ty).comptimeOnly(zcu)) {
16167 func_is_generic = true;
16168 }
16169
16170 const param_ty_val = try pt.intern(.{ .opt = .{
16171 .ty = try pt.intern(.{ .opt_type = .type_type }),
16172 .val = if (is_generic) .none else param_ty,
16173 } });
16174
16175 const param_attrs_fields = .{
16176 // @"noalias": bool,
16177 Value.makeBool(is_noalias).toIntern(),
16178 };
16179
16180 param_type_val.* = param_ty_val;
16181 param_attr_val.* = (try pt.aggregateValue(param_attrs_ty, &param_attrs_fields)).toIntern();
16182 }
16183
16184 const param_types_val = v: {
16185 const new_decl_ty = try pt.arrayType(.{
16186 .len = param_type_vals.len,
16187 .child = try pt.intern(.{ .opt_type = .type_type }),
16188 });
16189 const new_decl_val = (try pt.aggregateValue(new_decl_ty, param_type_vals)).toIntern();
16190 const slice_ty = (try pt.ptrType(.{
16191 .child = try pt.intern(.{ .opt_type = .type_type }),
16192 .flags = .{
16193 .size = .slice,
16194 .is_const = true,
16195 },
16196 })).toIntern();
16197 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
16198 break :v try pt.intern(.{ .slice = .{
16199 .ty = slice_ty,
16200 .ptr = try pt.intern(.{ .ptr = .{
16201 .ty = manyptr_ty,
16202 .base_addr = .{ .uav = .{
16203 .orig_ty = manyptr_ty,
16204 .val = new_decl_val,
16205 } },
16206 .byte_offset = 0,
16207 } }),
16208 .len = (try pt.intValue(.usize, param_type_vals.len)).toIntern(),
16209 } });
16210 };
16211 const param_attrs_val = v: {
16212 const new_decl_ty = try pt.arrayType(.{
16213 .len = param_attr_vals.len,
16214 .child = param_attrs_ty.toIntern(),
16215 });
16216 const new_decl_val = (try pt.aggregateValue(new_decl_ty, param_attr_vals)).toIntern();
16217 const slice_ty = (try pt.ptrType(.{
16218 .child = param_attrs_ty.toIntern(),
16219 .flags = .{
16220 .size = .slice,
16221 .is_const = true,
16222 },
16223 })).toIntern();
16224 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
16225 break :v try pt.intern(.{ .slice = .{
16226 .ty = slice_ty,
16227 .ptr = try pt.intern(.{ .ptr = .{
16228 .ty = manyptr_ty,
16229 .base_addr = .{ .uav = .{
16230 .orig_ty = manyptr_ty,
16231 .val = new_decl_val,
16232 } },
16233 .byte_offset = 0,
16234 } }),
16235 .len = (try pt.intValue(.usize, param_attr_vals.len)).toIntern(),
16236 } });
16237 };
16238
16239 const ret_ty_is_generic = generic: {
16240 const ret_ty: Type = .fromInterned(func_ty_info.return_type);
16241 if (ret_ty.toIntern() == .generic_poison_type or
16242 (ret_ty.zigTypeTag(zcu) == .error_union and
16243 ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type))
16244 {
16245 break :generic true;
16246 }
16247 break :generic false;
16248 };
16249 if (ret_ty_is_generic or Type.fromInterned(func_ty_info.return_type).comptimeOnly(zcu)) {
16250 func_is_generic = true;
16251 }
16252
16253 const ret_ty_opt = try pt.intern(.{ .opt = .{
16254 .ty = try pt.intern(.{ .opt_type = .type_type }),
16255 .val = if (ret_ty_is_generic) .none else func_ty_info.return_type,
16256 } });
16257
16258 const callconv_ty = try sema.getStdLangType(src, .CallingConvention);
16259 const callconv_val = Value.uninterpret(func_ty_info.cc, callconv_ty, pt) catch |err| switch (err) {
16260 error.TypeMismatch => @panic("std.lang is corrupt"),
16261 error.OutOfMemory => |e| return e,
16262 };
16263
16264 const fn_attrs_values = .{
16265 // @"callconv": CallingConvention = .auto,
16266 callconv_val.toIntern(),
16267 // varargs: bool = false,
16268 Value.makeBool(func_ty_info.is_var_args).toIntern(),
16269 };
16270
16271 const field_values = .{
16272 // attrs: Attributes,
16273 (try pt.aggregateValue(fn_attr_ty, &fn_attrs_values)).toIntern(),
16274 // is_generic: bool,
16275 Value.makeBool(func_is_generic).toIntern(),
16276 // return_type: ?type,
16277 ret_ty_opt,
16278
16279 // param_types: []const ?type,
16280 param_types_val,
16281 // param_attrs: []const ParamAttributes,
16282 param_attrs_val,
16283 };
16284 return Air.internedToRef((try pt.internUnion(.{
16285 .ty = type_info_ty.toIntern(),
16286 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"fn"))).toIntern(),
16287 .val = (try pt.aggregateValue(fn_info_ty, &field_values)).toIntern(),
16288 })));
16289 },
16290 .int => {
16291 const int_info_ty = try sema.getStdLangType(src, .@"Type.Int");
16292 const signedness_ty = try sema.getStdLangType(src, .Signedness);
16293 const info = ty.intInfo(zcu);
16294 const field_values = .{
16295 // signedness: Signedness,
16296 (try pt.enumValueFieldIndex(signedness_ty, @backingInt(info.signedness))).toIntern(),
16297 // bits: u16,
16298 (try pt.intValue(.u16, info.bits)).toIntern(),
16299 };
16300 return Air.internedToRef((try pt.internUnion(.{
16301 .ty = type_info_ty.toIntern(),
16302 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.int))).toIntern(),
16303 .val = (try pt.aggregateValue(int_info_ty, &field_values)).toIntern(),
16304 })));
16305 },
16306 .float => {
16307 const float_info_ty = try sema.getStdLangType(src, .@"Type.Float");
16308
16309 const field_vals = .{
16310 // bits: u16,
16311 (try pt.intValue(.u16, ty.floatBits(zcu.getTarget()))).toIntern(),
16312 };
16313 return Air.internedToRef((try pt.internUnion(.{
16314 .ty = type_info_ty.toIntern(),
16315 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.float))).toIntern(),
16316 .val = (try pt.aggregateValue(float_info_ty, &field_vals)).toIntern(),
16317 })));
16318 },
16319 .pointer => {
16320 const info = ty.ptrInfo(zcu);
16321 const alignment_ty = try pt.optionalType(.usize_type);
16322 const alignment_val: Value = val: {
16323 const bytes = info.flags.alignment.toByteUnits() orelse {
16324 break :val try pt.nullValue(alignment_ty);
16325 };
16326 const int_val = try pt.intValue(.usize, bytes);
16327 break :val .fromInterned(try pt.intern(.{ .opt = .{
16328 .ty = alignment_ty.toIntern(),
16329 .val = int_val.toIntern(),
16330 } }));
16331 };
16332
16333 const addrspace_ty = try sema.getStdLangType(src, .AddressSpace);
16334 const pointer_ty = try sema.getStdLangType(src, .@"Type.Pointer");
16335 const ptr_size_ty = try sema.getStdLangType(src, .@"Type.Pointer.Size");
16336 const ptr_attrs_ty = try sema.getStdLangType(src, .@"Type.Pointer.Attributes");
16337
16338 const opt_addrspace_val = try pt.intern(.{ .opt = .{
16339 .ty = (try pt.optionalType(addrspace_ty.toIntern())).toIntern(),
16340 .val = (try sema.uninterpretStdLangType(info.flags.address_space, addrspace_ty)).toIntern(),
16341 } });
16342
16343 const attributes = .{
16344 // @"const": bool = false,
16345 Value.makeBool(info.flags.is_const).toIntern(),
16346 // @"volatile": bool = false,
16347 Value.makeBool(info.flags.is_volatile).toIntern(),
16348 // @"allowzero": bool = false,
16349 Value.makeBool(info.flags.is_allowzero).toIntern(),
16350 // @"addrspace": ?AddressSpace = null,
16351 opt_addrspace_val,
16352 // @"align": ?usize = null,
16353 alignment_val.toIntern(),
16354 };
16355
16356 const field_values = .{
16357 // size: Size,
16358 (try pt.enumValueFieldIndex(ptr_size_ty, @backingInt(info.flags.size))).toIntern(),
16359 // attrs: Attributes
16360 (try pt.aggregateValue(ptr_attrs_ty, &attributes)).toIntern(),
16361 // child: type,
16362 info.child,
16363 // sentinel_ptr: ?*const anyopaque,
16364 (try sema.optRefValue(switch (info.sentinel) {
16365 .none => null,
16366 else => Value.fromInterned(info.sentinel),
16367 })).toIntern(),
16368 };
16369 return Air.internedToRef((try pt.internUnion(.{
16370 .ty = type_info_ty.toIntern(),
16371 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.pointer))).toIntern(),
16372 .val = (try pt.aggregateValue(pointer_ty, &field_values)).toIntern(),
16373 })));
16374 },
16375 .array => {
16376 const array_field_ty = try sema.getStdLangType(src, .@"Type.Array");
16377
16378 const info = ty.arrayInfo(zcu);
16379 const field_values = .{
16380 // len: comptime_int,
16381 (try pt.intValue(.comptime_int, info.len)).toIntern(),
16382 // child: type,
16383 info.elem_type.toIntern(),
16384 // sentinel: ?*const anyopaque,
16385 (try sema.optRefValue(info.sentinel)).toIntern(),
16386 };
16387 return Air.internedToRef((try pt.internUnion(.{
16388 .ty = type_info_ty.toIntern(),
16389 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.array))).toIntern(),
16390 .val = (try pt.aggregateValue(array_field_ty, &field_values)).toIntern(),
16391 })));
16392 },
16393 .vector => {
16394 const vector_field_ty = try sema.getStdLangType(src, .@"Type.Vector");
16395
16396 const info = ty.arrayInfo(zcu);
16397 const field_values = .{
16398 // len: comptime_int,
16399 (try pt.intValue(.comptime_int, info.len)).toIntern(),
16400 // child: type,
16401 info.elem_type.toIntern(),
16402 };
16403 return Air.internedToRef((try pt.internUnion(.{
16404 .ty = type_info_ty.toIntern(),
16405 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.vector))).toIntern(),
16406 .val = (try pt.aggregateValue(vector_field_ty, &field_values)).toIntern(),
16407 })));
16408 },
16409 .optional => {
16410 const optional_field_ty = try sema.getStdLangType(src, .@"Type.Optional");
16411
16412 const field_values = .{
16413 // child: type,
16414 ty.optionalChild(zcu).toIntern(),
16415 };
16416 return Air.internedToRef((try pt.internUnion(.{
16417 .ty = type_info_ty.toIntern(),
16418 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.optional))).toIntern(),
16419 .val = (try pt.aggregateValue(optional_field_ty, &field_values)).toIntern(),
16420 })));
16421 },
16422 .error_set => {
16423 const error_set_ty = try sema.getStdLangType(src, .@"Type.ErrorSet");
16424
16425 // Build our list of Error values
16426 // Optional value is only null if anyerror
16427 // Value can be zero-length slice otherwise
16428 const error_field_vals = switch (try sema.resolveInferredErrorSetTy(block, src, ty.toIntern())) {
16429 .anyerror_type => null,
16430 else => |err_set_ty_index| blk: {
16431 const names = ip.indexToKey(err_set_ty_index).error_set_type.names;
16432 const vals = try sema.arena.alloc(InternPool.Index, names.len);
16433 for (vals, 0..) |*field_val, error_index| {
16434 const error_name = names.get(ip)[error_index];
16435 const error_name_len = error_name.length(ip);
16436 const error_name_val = v: {
16437 const new_decl_ty = try pt.arrayType(.{
16438 .len = error_name_len,
16439 .sentinel = .zero_u8,
16440 .child = .u8_type,
16441 });
16442 const new_decl_val = try pt.intern(.{ .aggregate = .{
16443 .ty = new_decl_ty.toIntern(),
16444 .storage = .{ .bytes = error_name.toString() },
16445 } });
16446 break :v try pt.intern(.{ .slice = .{
16447 .ty = .slice_const_u8_sentinel_0_type,
16448 .ptr = try pt.intern(.{ .ptr = .{
16449 .ty = .manyptr_const_u8_sentinel_0_type,
16450 .base_addr = .{ .uav = .{
16451 .val = new_decl_val,
16452 .orig_ty = .slice_const_u8_sentinel_0_type,
16453 } },
16454 .byte_offset = 0,
16455 } }),
16456 .len = (try pt.intValue(.usize, error_name_len)).toIntern(),
16457 } });
16458 };
16459
16460 field_val.* = error_name_val;
16461 }
16462
16463 break :blk vals;
16464 },
16465 };
16466
16467 // Build our ?[]const [:0]const u8 value
16468 const slice_errors_ty = try pt.ptrType(.{
16469 .child = .slice_const_u8_sentinel_0_type,
16470 .flags = .{
16471 .size = .slice,
16472 .is_const = true,
16473 },
16474 });
16475 const opt_slice_errors_ty = try pt.optionalType(slice_errors_ty.toIntern());
16476 const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: {
16477 const array_errors_ty = try pt.arrayType(.{
16478 .len = vals.len,
16479 .child = .slice_const_u8_sentinel_0_type,
16480 });
16481 const new_decl_val = (try pt.aggregateValue(array_errors_ty, vals)).toIntern();
16482 const manyptr_errors_ty = slice_errors_ty.slicePtrFieldType(zcu).toIntern();
16483 break :v try pt.intern(.{ .slice = .{
16484 .ty = slice_errors_ty.toIntern(),
16485 .ptr = try pt.intern(.{ .ptr = .{
16486 .ty = manyptr_errors_ty,
16487 .base_addr = .{ .uav = .{
16488 .orig_ty = manyptr_errors_ty,
16489 .val = new_decl_val,
16490 } },
16491 .byte_offset = 0,
16492 } }),
16493 .len = (try pt.intValue(.usize, vals.len)).toIntern(),
16494 } });
16495 } else .none;
16496 const errors_val = try pt.intern(.{ .opt = .{
16497 .ty = opt_slice_errors_ty.toIntern(),
16498 .val = errors_payload_val,
16499 } });
16500
16501 const field_values = .{
16502 // error_names: ?[]const [:0]const u8
16503 errors_val,
16504 };
16505
16506 // Construct Type{ .error_set = errors_val }
16507 return Air.internedToRef((try pt.internUnion(.{
16508 .ty = type_info_ty.toIntern(),
16509 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.error_set))).toIntern(),
16510 .val = (try pt.aggregateValue(error_set_ty, &field_values)).toIntern(),
16511 })));
16512 },
16513 .error_union => {
16514 const error_union_field_ty = try sema.getStdLangType(src, .@"Type.ErrorUnion");
16515
16516 const field_values = .{
16517 // error_set: type,
16518 ty.errorUnionSet(zcu).toIntern(),
16519 // payload: type,
16520 ty.errorUnionPayload(zcu).toIntern(),
16521 };
16522 return Air.internedToRef((try pt.internUnion(.{
16523 .ty = type_info_ty.toIntern(),
16524 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.error_union))).toIntern(),
16525 .val = (try pt.aggregateValue(error_union_field_ty, &field_values)).toIntern(),
16526 })));
16527 },
16528 .@"enum" => {
16529 const enum_obj = ip.loadEnumType(ty.toIntern());
16530
16531 const enum_mode_ty = try sema.getStdLangType(src, .@"Type.Enum.Mode");
16532
16533 const enum_mode_tag: std.builtin.Type.Enum.Mode = if (enum_obj.nonexhaustive) .nonexhaustive else .exhaustive;
16534
16535 const enum_mode: Value = try sema.uninterpretStdLangType(enum_mode_tag, enum_mode_ty);
16536
16537 const enum_field_name_vals = try sema.arena.alloc(InternPool.Index, enum_obj.field_names.len);
16538 const enum_field_value_vals = try sema.arena.alloc(InternPool.Index, enum_obj.field_names.len);
16539 for (
16540 enum_field_name_vals,
16541 enum_field_value_vals,
16542 0..,
16543 ) |*field_name_val, *field_value_val, tag_index| {
16544 const value_val = if (enum_obj.field_values.len > 0)
16545 try ip.getCoercedInts(
16546 gpa,
16547 io,
16548 pt.tid,
16549 ip.indexToKey(enum_obj.field_values.get(ip)[tag_index]).int,
16550 .comptime_int_type,
16551 )
16552 else
16553 (try pt.intValue(.comptime_int, tag_index)).toIntern();
16554
16555 // TODO: write something like getCoercedInts to avoid needing to dupe
16556 const name_val = v: {
16557 const tag_name = enum_obj.field_names.get(ip)[tag_index];
16558 const tag_name_len = tag_name.length(ip);
16559 const new_decl_ty = try pt.arrayType(.{
16560 .len = tag_name_len,
16561 .sentinel = .zero_u8,
16562 .child = .u8_type,
16563 });
16564 const new_decl_val = try pt.intern(.{ .aggregate = .{
16565 .ty = new_decl_ty.toIntern(),
16566 .storage = .{ .bytes = tag_name.toString() },
16567 } });
16568 break :v try pt.intern(.{ .slice = .{
16569 .ty = .slice_const_u8_sentinel_0_type,
16570 .ptr = try pt.intern(.{ .ptr = .{
16571 .ty = .manyptr_const_u8_sentinel_0_type,
16572 .base_addr = .{ .uav = .{
16573 .val = new_decl_val,
16574 .orig_ty = .slice_const_u8_sentinel_0_type,
16575 } },
16576 .byte_offset = 0,
16577 } }),
16578 .len = (try pt.intValue(.usize, tag_name_len)).toIntern(),
16579 } });
16580 };
16581
16582 field_name_val.* = name_val;
16583 field_value_val.* = value_val;
16584 }
16585
16586 const fields_names_val = v: {
16587 const fields_names_array_ty = try pt.arrayType(.{
16588 .len = enum_field_name_vals.len,
16589 .child = .slice_const_u8_sentinel_0_type,
16590 });
16591 const new_decl_val = (try pt.aggregateValue(fields_names_array_ty, enum_field_name_vals)).toIntern();
16592 const slice_ty = (try pt.ptrType(.{
16593 .child = .slice_const_u8_sentinel_0_type,
16594 .flags = .{
16595 .size = .slice,
16596 .is_const = true,
16597 },
16598 })).toIntern();
16599 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
16600 break :v try pt.intern(.{ .slice = .{
16601 .ty = slice_ty,
16602 .ptr = try pt.intern(.{ .ptr = .{
16603 .ty = manyptr_ty,
16604 .base_addr = .{ .uav = .{
16605 .val = new_decl_val,
16606 .orig_ty = manyptr_ty,
16607 } },
16608 .byte_offset = 0,
16609 } }),
16610 .len = (try pt.intValue(.usize, enum_field_name_vals.len)).toIntern(),
16611 } });
16612 };
16613
16614 const fields_values_val = v: {
16615 const fields_values_array_ty = try pt.arrayType(.{
16616 .len = enum_field_value_vals.len,
16617 .child = .comptime_int_type,
16618 });
16619 const new_decl_val = (try pt.aggregateValue(fields_values_array_ty, enum_field_value_vals)).toIntern();
16620 const slice_ty = (try pt.ptrType(.{
16621 .child = .comptime_int_type,
16622 .flags = .{
16623 .size = .slice,
16624 .is_const = true,
16625 },
16626 })).toIntern();
16627 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
16628 break :v try pt.intern(.{ .slice = .{
16629 .ty = slice_ty,
16630 .ptr = try pt.intern(.{ .ptr = .{
16631 .ty = manyptr_ty,
16632 .base_addr = .{ .uav = .{
16633 .val = new_decl_val,
16634 .orig_ty = manyptr_ty,
16635 } },
16636 .byte_offset = 0,
16637 } }),
16638 .len = (try pt.intValue(.usize, enum_field_value_vals.len)).toIntern(),
16639 } });
16640 };
16641
16642 const decl_names_val = try sema.typeInfoDecls(ip.loadEnumType(ty.toIntern()).namespace.toOptional());
16643
16644 const type_enum_ty = try sema.getStdLangType(src, .@"Type.Enum");
16645
16646 const field_values = .{
16647 // tag_type: type,
16648 ip.loadEnumType(ty.toIntern()).int_tag_type,
16649 // mode: Mode
16650 enum_mode.toIntern(),
16651
16652 // field_names: []const [:0]const u8,
16653 fields_names_val,
16654 // field_values: []const comptime_int,
16655 fields_values_val,
16656
16657 // decl_names: []const [:0]const u8,
16658 decl_names_val,
16659 };
16660 return Air.internedToRef((try pt.internUnion(.{
16661 .ty = type_info_ty.toIntern(),
16662 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"enum"))).toIntern(),
16663 .val = (try pt.aggregateValue(type_enum_ty, &field_values)).toIntern(),
16664 })));
16665 },
16666 .@"union" => {
16667 const type_union_ty = try sema.getStdLangType(src, .@"Type.Union");
16668 const union_field_attr_ty = try sema.getStdLangType(src, .@"Type.Union.FieldAttributes");
16669
16670 const union_obj = ip.loadUnionType(ty.toIntern());
16671 const enum_obj = ip.loadEnumType(union_obj.enum_tag_type);
16672 const layout = union_obj.layout;
16673
16674 const union_field_names = try gpa.alloc(InternPool.Index, enum_obj.field_names.len);
16675 defer gpa.free(union_field_names);
16676 const union_field_attrs = try gpa.alloc(InternPool.Index, enum_obj.field_names.len);
16677 defer gpa.free(union_field_attrs);
16678
16679 for (
16680 union_field_names,
16681 union_field_attrs,
16682 0..,
16683 ) |
16684 *field_name_val,
16685 *field_attr_val,
16686 field_index,
16687 | {
16688 const name_val = v: {
16689 const field_name = enum_obj.field_names.get(ip)[field_index];
16690 const field_name_len = field_name.length(ip);
16691 const new_decl_ty = try pt.arrayType(.{
16692 .len = field_name_len,
16693 .sentinel = .zero_u8,
16694 .child = .u8_type,
16695 });
16696 const new_decl_val = try pt.intern(.{ .aggregate = .{
16697 .ty = new_decl_ty.toIntern(),
16698 .storage = .{ .bytes = field_name.toString() },
16699 } });
16700 break :v try pt.intern(.{ .slice = .{
16701 .ty = .slice_const_u8_sentinel_0_type,
16702 .ptr = try pt.intern(.{ .ptr = .{
16703 .ty = .manyptr_const_u8_sentinel_0_type,
16704 .base_addr = .{ .uav = .{
16705 .val = new_decl_val,
16706 .orig_ty = .slice_const_u8_sentinel_0_type,
16707 } },
16708 .byte_offset = 0,
16709 } }),
16710 .len = (try pt.intValue(.usize, field_name_len)).toIntern(),
16711 } });
16712 };
16713
16714 const alignment_ty = try pt.optionalType(.usize_type);
16715 const alignment_val: Value = val: {
16716 const a: Alignment = switch (layout) {
16717 .auto, .@"extern" => ty.explicitFieldAlignment(field_index, zcu),
16718 .@"packed" => .none,
16719 };
16720 const bytes = a.toByteUnits() orelse {
16721 break :val try pt.nullValue(alignment_ty);
16722 };
16723 const int_val = try pt.intValue(.usize, bytes);
16724 break :val .fromInterned(try pt.intern(.{ .opt = .{
16725 .ty = alignment_ty.toIntern(),
16726 .val = int_val.toIntern(),
16727 } }));
16728 };
16729
16730 field_name_val.* = name_val;
16731 const union_field_attr = .{
16732 // alignment: ?usize,
16733 alignment_val.toIntern(),
16734 };
16735
16736 field_attr_val.* = (try pt.aggregateValue(union_field_attr_ty, &union_field_attr)).toIntern();
16737 }
16738
16739 const field_names_val = v: {
16740 const array_field_names_ty = try pt.arrayType(.{
16741 .len = union_field_names.len,
16742 .child = .slice_const_u8_sentinel_0_type,
16743 });
16744 const new_decl_val = (try pt.aggregateValue(array_field_names_ty, union_field_names)).toIntern();
16745 const slice_ty = (try pt.ptrType(.{
16746 .child = .slice_const_u8_sentinel_0_type,
16747 .flags = .{
16748 .size = .slice,
16749 .is_const = true,
16750 },
16751 })).toIntern();
16752 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
16753 break :v try pt.intern(.{ .slice = .{
16754 .ty = slice_ty,
16755 .ptr = try pt.intern(.{ .ptr = .{
16756 .ty = manyptr_ty,
16757 .base_addr = .{ .uav = .{
16758 .orig_ty = manyptr_ty,
16759 .val = new_decl_val,
16760 } },
16761 .byte_offset = 0,
16762 } }),
16763 .len = (try pt.intValue(.usize, union_field_names.len)).toIntern(),
16764 } });
16765 };
16766 const field_types_val = v: {
16767 const union_field_types = union_obj.field_types.get(ip);
16768
16769 const array_fields_ty = try pt.arrayType(.{
16770 .len = union_field_types.len,
16771 .child = .type_type,
16772 });
16773 const new_decl_val = (try pt.aggregateValue(array_fields_ty, union_field_types)).toIntern();
16774 const slice_ty = (try pt.ptrType(.{
16775 .child = .type_type,
16776 .flags = .{
16777 .size = .slice,
16778 .is_const = true,
16779 },
16780 })).toIntern();
16781 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
16782 break :v try pt.intern(.{ .slice = .{
16783 .ty = slice_ty,
16784 .ptr = try pt.intern(.{ .ptr = .{
16785 .ty = manyptr_ty,
16786 .base_addr = .{ .uav = .{
16787 .orig_ty = manyptr_ty,
16788 .val = new_decl_val,
16789 } },
16790 .byte_offset = 0,
16791 } }),
16792 .len = (try pt.intValue(.usize, union_field_types.len)).toIntern(),
16793 } });
16794 };
16795 const field_attrs_val = v: {
16796 const array_fields_ty = try pt.arrayType(.{
16797 .len = union_field_attrs.len,
16798 .child = union_field_attr_ty.toIntern(),
16799 });
16800 const new_decl_val = (try pt.aggregateValue(array_fields_ty, union_field_attrs)).toIntern();
16801 const slice_ty = (try pt.ptrType(.{
16802 .child = union_field_attr_ty.toIntern(),
16803 .flags = .{
16804 .size = .slice,
16805 .is_const = true,
16806 },
16807 })).toIntern();
16808 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
16809 break :v try pt.intern(.{ .slice = .{
16810 .ty = slice_ty,
16811 .ptr = try pt.intern(.{ .ptr = .{
16812 .ty = manyptr_ty,
16813 .base_addr = .{ .uav = .{
16814 .orig_ty = manyptr_ty,
16815 .val = new_decl_val,
16816 } },
16817 .byte_offset = 0,
16818 } }),
16819 .len = (try pt.intValue(.usize, union_field_attrs.len)).toIntern(),
16820 } });
16821 };
16822
16823 const decl_names_val = try sema.typeInfoDecls(ty.getNamespaceIndex(zcu).toOptional());
16824
16825 const enum_tag_ty_val = try pt.intern(.{ .opt = .{
16826 .ty = (try pt.optionalType(.type_type)).toIntern(),
16827 .val = if (ty.unionTagType(zcu)) |tag_ty| tag_ty.toIntern() else .none,
16828 } });
16829
16830 const container_layout_ty = try sema.getStdLangType(src, .@"Type.ContainerLayout");
16831
16832 const backing_integer_val = try pt.intern(.{ .opt = .{
16833 .ty = (try pt.optionalType(.type_type)).toIntern(),
16834 .val = if (layout == .@"packed") val: {
16835 assert(Type.fromInterned(union_obj.packed_backing_int_type).isInt(zcu));
16836 break :val union_obj.packed_backing_int_type;
16837 } else .none,
16838 } });
16839
16840 const field_values = .{
16841 // layout: ContainerLayout,
16842 (try pt.enumValueFieldIndex(container_layout_ty, @backingInt(layout))).toIntern(),
16843
16844 // tag_type: ?type,
16845 enum_tag_ty_val,
16846 // backing_integer: ?type,
16847 backing_integer_val,
16848
16849 // field_names: []const [:0]const u8,
16850 field_names_val,
16851 // field_types: []const type,
16852 field_types_val,
16853 // field_attrs: []const FieldAttributes,
16854 field_attrs_val,
16855
16856 // decl_names: []const [:0]const u8,
16857 decl_names_val,
16858 };
16859 return Air.internedToRef((try pt.internUnion(.{
16860 .ty = type_info_ty.toIntern(),
16861 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"union"))).toIntern(),
16862 .val = (try pt.aggregateValue(type_union_ty, &field_values)).toIntern(),
16863 })));
16864 },
16865 .@"struct" => {
16866 const type_struct_ty = try sema.getStdLangType(src, .@"Type.Struct");
16867 const struct_field_attr_ty = try sema.getStdLangType(src, .@"Type.Struct.FieldAttributes");
16868
16869 var struct_field_name_vals: []InternPool.Index = &.{};
16870 defer gpa.free(struct_field_name_vals);
16871 var struct_field_attr_vals: []InternPool.Index = &.{};
16872 defer gpa.free(struct_field_attr_vals);
16873 fv: {
16874 const struct_type = switch (ip.indexToKey(ty.toIntern())) {
16875 .tuple_type => |tuple_type| {
16876 struct_field_name_vals = try gpa.alloc(InternPool.Index, tuple_type.types.len);
16877 struct_field_attr_vals = try gpa.alloc(InternPool.Index, tuple_type.types.len);
16878 for (
16879 struct_field_name_vals,
16880 struct_field_attr_vals,
16881 0..,
16882 ) |
16883 *struct_field_name_val,
16884 *struct_field_attr_val,
16885 field_index,
16886 | {
16887 const field_val = tuple_type.values.get(ip)[field_index];
16888 const name_val = v: {
16889 const field_name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
16890 const field_name_len = field_name.length(ip);
16891 const new_decl_ty = try pt.arrayType(.{
16892 .len = field_name_len,
16893 .sentinel = .zero_u8,
16894 .child = .u8_type,
16895 });
16896 const new_decl_val = try pt.intern(.{ .aggregate = .{
16897 .ty = new_decl_ty.toIntern(),
16898 .storage = .{ .bytes = field_name.toString() },
16899 } });
16900 break :v try pt.intern(.{ .slice = .{
16901 .ty = .slice_const_u8_sentinel_0_type,
16902 .ptr = try pt.intern(.{ .ptr = .{
16903 .ty = .manyptr_const_u8_sentinel_0_type,
16904 .base_addr = .{ .uav = .{
16905 .val = new_decl_val,
16906 .orig_ty = .slice_const_u8_sentinel_0_type,
16907 } },
16908 .byte_offset = 0,
16909 } }),
16910 .len = (try pt.intValue(.usize, field_name_len)).toIntern(),
16911 } });
16912 };
16913
16914 const is_comptime = field_val != .none;
16915 const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null;
16916 const default_val_ptr = try sema.optRefValue(opt_default_val);
16917
16918 const struct_field_attr_fields = .{
16919 // @"comptime": bool,
16920 Value.makeBool(is_comptime).toIntern(),
16921 // @"align": ?usize,
16922 (try pt.nullValue(try pt.optionalType(.usize_type))).toIntern(),
16923 // default_value_ptr: ?*const anyopaque,
16924 default_val_ptr.toIntern(),
16925 };
16926
16927 struct_field_name_val.* = name_val;
16928 struct_field_attr_val.* = (try pt.aggregateValue(struct_field_attr_ty, &struct_field_attr_fields)).toIntern();
16929 }
16930 break :fv;
16931 },
16932 .struct_type => ip.loadStructType(ty.toIntern()),
16933 else => unreachable,
16934 };
16935 try sema.ensureStructDefaultsResolved(ty, src); // can't do this sooner, since it's not allowed on tuples
16936 struct_field_name_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
16937 struct_field_attr_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len);
16938
16939 for (
16940 struct_field_name_vals,
16941 struct_field_attr_vals,
16942 0..,
16943 ) |
16944 *field_name_val,
16945 *field_attr_val,
16946 field_index,
16947 | {
16948 const field_name = struct_type.field_names.get(ip)[field_index];
16949 const field_name_len = field_name.length(ip);
16950 const field_default: InternPool.Index = if (struct_type.field_defaults.len > 0) d: {
16951 break :d struct_type.field_defaults.get(ip)[field_index];
16952 } else .none;
16953 const field_is_comptime = struct_type.field_is_comptime_bits.get(ip, field_index);
16954 const name_val = v: {
16955 const new_decl_ty = try pt.arrayType(.{
16956 .len = field_name_len,
16957 .sentinel = .zero_u8,
16958 .child = .u8_type,
16959 });
16960 const new_decl_val = try pt.intern(.{ .aggregate = .{
16961 .ty = new_decl_ty.toIntern(),
16962 .storage = .{ .bytes = field_name.toString() },
16963 } });
16964 break :v try pt.intern(.{ .slice = .{
16965 .ty = .slice_const_u8_sentinel_0_type,
16966 .ptr = try pt.intern(.{ .ptr = .{
16967 .ty = .manyptr_const_u8_sentinel_0_type,
16968 .base_addr = .{ .uav = .{
16969 .val = new_decl_val,
16970 .orig_ty = .slice_const_u8_sentinel_0_type,
16971 } },
16972 .byte_offset = 0,
16973 } }),
16974 .len = (try pt.intValue(.usize, field_name_len)).toIntern(),
16975 } });
16976 };
16977
16978 const opt_default_val: ?Value = if (field_default == .none) null else .fromInterned(field_default);
16979 const default_val_ptr = try sema.optRefValue(opt_default_val);
16980
16981 const alignment_ty = try pt.optionalType(.usize_type);
16982 const alignment_val: Value = val: {
16983 const a: Alignment = switch (struct_type.layout) {
16984 .auto, .@"extern" => ty.explicitFieldAlignment(field_index, zcu),
16985 .@"packed" => .none,
16986 };
16987 const bytes = a.toByteUnits() orelse {
16988 break :val try pt.nullValue(alignment_ty);
16989 };
16990 const int_val = try pt.intValue(.usize, bytes);
16991 break :val .fromInterned(try pt.intern(.{ .opt = .{
16992 .ty = alignment_ty.toIntern(),
16993 .val = int_val.toIntern(),
16994 } }));
16995 };
16996
16997 const struct_field_attr_fields = .{
16998 // @"comptime": bool,
16999 Value.makeBool(field_is_comptime).toIntern(),
17000 // @"align": ?usize,
17001 alignment_val.toIntern(),
17002 // default_value_ptr: ?*const anyopaque,
17003 default_val_ptr.toIntern(),
17004 };
17005 field_name_val.* = name_val;
17006 field_attr_val.* = (try pt.aggregateValue(struct_field_attr_ty, &struct_field_attr_fields)).toIntern();
17007 }
17008 }
17009
17010 const field_names_val = v: {
17011 const array_fields_ty = try pt.arrayType(.{
17012 .len = struct_field_name_vals.len,
17013 .child = .slice_const_u8_sentinel_0_type,
17014 });
17015 const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_name_vals)).toIntern();
17016 const slice_ty = (try pt.ptrType(.{
17017 .child = .slice_const_u8_sentinel_0_type,
17018 .flags = .{
17019 .size = .slice,
17020 .is_const = true,
17021 },
17022 })).toIntern();
17023 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
17024 break :v try pt.intern(.{ .slice = .{
17025 .ty = slice_ty,
17026 .ptr = try pt.intern(.{ .ptr = .{
17027 .ty = manyptr_ty,
17028 .base_addr = .{ .uav = .{
17029 .orig_ty = manyptr_ty,
17030 .val = new_decl_val,
17031 } },
17032 .byte_offset = 0,
17033 } }),
17034 .len = (try pt.intValue(.usize, struct_field_name_vals.len)).toIntern(),
17035 } });
17036 };
17037
17038 const field_types_val = v: {
17039 const struct_field_type_vals = switch (ip.indexToKey(ty.toIntern())) {
17040 .tuple_type => |tt| tt.types.get(ip),
17041 .struct_type => blk: {
17042 const st = ip.loadStructType(ty.toIntern());
17043 break :blk st.field_types.get(ip);
17044 },
17045 else => unreachable,
17046 };
17047 const array_fields_ty = try pt.arrayType(.{
17048 .len = struct_field_type_vals.len,
17049 .child = .type_type,
17050 });
17051 const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_type_vals)).toIntern();
17052 const slice_ty = (try pt.ptrType(.{
17053 .child = .type_type,
17054 .flags = .{
17055 .size = .slice,
17056 .is_const = true,
17057 },
17058 })).toIntern();
17059 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
17060 break :v try pt.intern(.{ .slice = .{
17061 .ty = slice_ty,
17062 .ptr = try pt.intern(.{ .ptr = .{
17063 .ty = manyptr_ty,
17064 .base_addr = .{ .uav = .{
17065 .orig_ty = manyptr_ty,
17066 .val = new_decl_val,
17067 } },
17068 .byte_offset = 0,
17069 } }),
17070 .len = (try pt.intValue(.usize, struct_field_type_vals.len)).toIntern(),
17071 } });
17072 };
17073 const field_attrs_val = v: {
17074 const array_fields_ty = try pt.arrayType(.{
17075 .len = struct_field_attr_vals.len,
17076 .child = struct_field_attr_ty.toIntern(),
17077 });
17078 const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_attr_vals)).toIntern();
17079 const slice_ty = (try pt.ptrType(.{
17080 .child = struct_field_attr_ty.toIntern(),
17081 .flags = .{
17082 .size = .slice,
17083 .is_const = true,
17084 },
17085 })).toIntern();
17086 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
17087 break :v try pt.intern(.{ .slice = .{
17088 .ty = slice_ty,
17089 .ptr = try pt.intern(.{ .ptr = .{
17090 .ty = manyptr_ty,
17091 .base_addr = .{ .uav = .{
17092 .orig_ty = manyptr_ty,
17093 .val = new_decl_val,
17094 } },
17095 .byte_offset = 0,
17096 } }),
17097 .len = (try pt.intValue(.usize, struct_field_attr_vals.len)).toIntern(),
17098 } });
17099 };
17100
17101 const decl_names_val = try sema.typeInfoDecls(ty.getNamespace(zcu));
17102
17103 const backing_integer_val = try pt.intern(.{ .opt = .{
17104 .ty = (try pt.optionalType(.type_type)).toIntern(),
17105 .val = if (zcu.typeToPackedStruct(ty)) |struct_obj| val: {
17106 assert(Type.fromInterned(struct_obj.packed_backing_int_type).isInt(zcu));
17107 break :val struct_obj.packed_backing_int_type;
17108 } else .none,
17109 } });
17110
17111 const container_layout_ty = try sema.getStdLangType(src, .@"Type.ContainerLayout");
17112
17113 const layout = ty.containerLayout(zcu);
17114
17115 const field_values = [_]InternPool.Index{
17116 // is_tuple: bool,
17117 Value.makeBool(ty.isTuple(zcu)).toIntern(),
17118 // layout: ContainerLayout,
17119 (try pt.enumValueFieldIndex(container_layout_ty, @backingInt(layout))).toIntern(),
17120 // backing_integer: ?type,
17121 backing_integer_val,
17122
17123 // field_names: []const [:0]const u8,
17124 field_names_val,
17125 // field_types: []const type,
17126 field_types_val,
17127 // field_attrs: []const FieldAttributes,
17128 field_attrs_val,
17129
17130 // decl_names: []const [:0]const u8,
17131 decl_names_val,
17132 };
17133 return Air.internedToRef((try pt.internUnion(.{
17134 .ty = type_info_ty.toIntern(),
17135 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"struct"))).toIntern(),
17136 .val = (try pt.aggregateValue(type_struct_ty, &field_values)).toIntern(),
17137 })));
17138 },
17139 .@"opaque" => {
17140 const type_opaque_ty = try sema.getStdLangType(src, .@"Type.Opaque");
17141
17142 const decl_names_val = try sema.typeInfoDecls(ty.getNamespace(zcu));
17143
17144 const field_values = .{
17145 // decl_names: []const [:0]const u8,
17146 decl_names_val,
17147 };
17148 return Air.internedToRef((try pt.internUnion(.{
17149 .ty = type_info_ty.toIntern(),
17150 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"opaque"))).toIntern(),
17151 .val = (try pt.aggregateValue(type_opaque_ty, &field_values)).toIntern(),
17152 })));
17153 },
17154 .spirv => {
17155 const spirv_info = ip.loadSpirvType(ty.toIntern());
17156 const spirv_union_ty = try sema.getStdLangType(src, .@"Type.Spirv");
17157 const spirv_tag_ty = spirv_union_ty.unionTagType(zcu).?;
17158 const spirv_tag_val = try pt.enumValueFieldIndex(spirv_tag_ty, @backingInt(spirv_info.flags.tag));
17159 const spirv_payload_val: Value = switch (spirv_info.flags.tag) {
17160 .sampler => .void,
17161 .sampled_image, .runtime_array => .fromInterned(spirv_info.ty),
17162 .image => image: {
17163 const image_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image");
17164 const usage_union_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Usage");
17165 const format_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Format");
17166 const dim_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Dimensionality");
17167 const depth_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Depth");
17168 const access_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Access");
17169 const usage_tag_ty = usage_union_ty.unionTagType(zcu).?;
17170 const usage_tag_val = try pt.enumValueFieldIndex(usage_tag_ty, @backingInt(spirv_info.flags.usage));
17171 const usage_val = try pt.unionValue(usage_union_ty, usage_tag_val, .fromInterned(spirv_info.ty));
17172 const image_field_vals = [_]InternPool.Index{
17173 usage_val.toIntern(),
17174 (try pt.enumValueFieldIndex(format_ty, @backingInt(spirv_info.flags.format))).toIntern(),
17175 (try pt.enumValueFieldIndex(dim_ty, @backingInt(spirv_info.flags.dim))).toIntern(),
17176 (try pt.enumValueFieldIndex(depth_ty, @backingInt(spirv_info.flags.depth))).toIntern(),
17177 (try pt.enumValueFieldIndex(access_ty, @backingInt(spirv_info.flags.access))).toIntern(),
17178 Value.makeBool(spirv_info.flags.is_arrayed).toIntern(),
17179 Value.makeBool(spirv_info.flags.is_multisampled).toIntern(),
17180 };
17181 break :image try pt.aggregateValue(image_ty, &image_field_vals);
17182 },
17183 };
17184 const spirv_val = try pt.unionValue(spirv_union_ty, spirv_tag_val, spirv_payload_val);
17185 return Air.internedToRef((try pt.internUnion(.{
17186 .ty = type_info_ty.toIntern(),
17187 .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.spirv))).toIntern(),
17188 .val = spirv_val.toIntern(),
17189 })));
17190 },
17191 .frame => return sema.failWithUseOfAsync(block, src),
17192 .@"anyframe" => return sema.failWithUseOfAsync(block, src),
17193 }
17194}
17195
17196fn typeInfoDecls(
17197 sema: *Sema,
17198 opt_namespace: InternPool.OptionalNamespaceIndex,
17199) CompileError!InternPool.Index {
17200 const pt = sema.pt;
17201 const zcu = pt.zcu;
17202 const gpa = sema.gpa;
17203
17204 var decl_vals = std.array_list.Managed(InternPool.Index).init(gpa);
17205 defer decl_vals.deinit();
17206
17207 var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa);
17208 defer seen_namespaces.deinit();
17209
17210 try sema.typeInfoNamespaceDecls(opt_namespace, &decl_vals, &seen_namespaces);
17211
17212 const array_decl_ty = try pt.arrayType(.{
17213 .len = decl_vals.items.len,
17214 .child = .slice_const_u8_sentinel_0_type,
17215 });
17216 const new_decl_val = (try pt.aggregateValue(array_decl_ty, decl_vals.items)).toIntern();
17217 const slice_ty = (try pt.ptrType(.{
17218 .child = .slice_const_u8_sentinel_0_type,
17219 .flags = .{
17220 .size = .slice,
17221 .is_const = true,
17222 },
17223 })).toIntern();
17224 const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern();
17225 return try pt.intern(.{ .slice = .{
17226 .ty = slice_ty,
17227 .ptr = try pt.intern(.{ .ptr = .{
17228 .ty = manyptr_ty,
17229 .base_addr = .{ .uav = .{
17230 .orig_ty = manyptr_ty,
17231 .val = new_decl_val,
17232 } },
17233 .byte_offset = 0,
17234 } }),
17235 .len = (try pt.intValue(.usize, decl_vals.items.len)).toIntern(),
17236 } });
17237}
17238
17239fn typeInfoNamespaceDecls(
17240 sema: *Sema,
17241 opt_namespace_index: InternPool.OptionalNamespaceIndex,
17242 decl_vals: *std.array_list.Managed(InternPool.Index),
17243 seen_namespaces: *std.AutoHashMap(*Namespace, void),
17244) !void {
17245 const pt = sema.pt;
17246 const zcu = pt.zcu;
17247 const ip = &zcu.intern_pool;
17248
17249 const namespace_index = opt_namespace_index.unwrap() orelse return;
17250 pt.ensureNamespaceUpToDate(namespace_index) catch |err| switch (err) {
17251 error.LostZirContainerDecl => {
17252 const namespace = zcu.namespacePtr(namespace_index);
17253 const ns_ty: Type = .fromInterned(namespace.owner_type);
17254 return sema.failTransitive(.{ .lost_tracking = ns_ty.typeDeclInstAllowGeneratedTag(zcu).? });
17255 },
17256 else => |e| return e,
17257 };
17258 const namespace = zcu.namespacePtr(namespace_index);
17259
17260 const gop = try seen_namespaces.getOrPut(namespace);
17261 if (gop.found_existing) return;
17262
17263 for (namespace.pub_decls.keys()) |nav| {
17264 const name = ip.getNav(nav).name;
17265 const name_val = name_val: {
17266 const name_len = name.length(ip);
17267 const array_ty = try pt.arrayType(.{
17268 .len = name_len,
17269 .sentinel = .zero_u8,
17270 .child = .u8_type,
17271 });
17272 const array_val = try pt.intern(.{ .aggregate = .{
17273 .ty = array_ty.toIntern(),
17274 .storage = .{ .bytes = name.toString() },
17275 } });
17276 break :name_val try pt.intern(.{
17277 .slice = .{
17278 .ty = .slice_const_u8_sentinel_0_type, // [:0]const u8
17279 .ptr = try pt.intern(.{
17280 .ptr = .{
17281 .ty = .manyptr_const_u8_sentinel_0_type, // [*:0]const u8
17282 .base_addr = .{ .uav = .{
17283 .orig_ty = .slice_const_u8_sentinel_0_type,
17284 .val = array_val,
17285 } },
17286 .byte_offset = 0,
17287 },
17288 }),
17289 .len = (try pt.intValue(.usize, name_len)).toIntern(),
17290 },
17291 });
17292 };
17293 try decl_vals.append(name_val);
17294 }
17295}
17296
17297fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17298 _ = block;
17299 const zir_datas = sema.code.instructions.items(.data);
17300 const inst_data = zir_datas[@backingInt(inst)].un_node;
17301 const operand = sema.resolveInst(inst_data.operand);
17302 const operand_ty = sema.typeOf(operand);
17303 return Air.internedToRef(operand_ty.toIntern());
17304}
17305
17306fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17307 const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
17308 const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
17309 const body = sema.code.bodySlice(extra.end, extra.data.body_len);
17310
17311 var child_block: Block = .{
17312 .parent = block,
17313 .sema = sema,
17314 .namespace = block.namespace,
17315 .instructions = .empty,
17316 .inlining = block.inlining,
17317 .comptime_reason = null,
17318 .is_typeof = true,
17319 .want_safety = false,
17320 .error_return_trace_index = block.error_return_trace_index,
17321 .src_base_inst = block.src_base_inst,
17322 .type_name_ctx = block.type_name_ctx,
17323 .type_fqn_ctx = block.type_fqn_ctx,
17324 };
17325 defer child_block.instructions.deinit(sema.gpa);
17326
17327 const operand = try sema.resolveInlineBody(&child_block, body, inst);
17328 return Air.internedToRef(sema.typeOf(operand).toIntern());
17329}
17330
17331fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17332 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
17333 const src = block.nodeOffset(inst_data.src_node);
17334 const operand = sema.resolveInst(inst_data.operand);
17335 const operand_ty = sema.typeOf(operand);
17336 const res_ty = try sema.log2IntType(block, operand_ty, src);
17337 return Air.internedToRef(res_ty.toIntern());
17338}
17339
17340fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type {
17341 const pt = sema.pt;
17342 const zcu = pt.zcu;
17343 switch (operand.zigTypeTag(zcu)) {
17344 .comptime_int => return .comptime_int,
17345 .int => return pt.intType(.unsigned, switch (operand.intInfo(zcu).bits) {
17346 0 => 0,
17347 else => |b| std.math.log2_int_ceil(u16, b),
17348 }),
17349 .vector => {
17350 const elem_ty = operand.childType(zcu);
17351 const log2_elem_ty = try sema.log2IntType(block, elem_ty, src);
17352 return pt.vectorType(.{
17353 .len = operand.vectorLen(zcu),
17354 .child = log2_elem_ty.toIntern(),
17355 });
17356 },
17357 else => {},
17358 }
17359 return sema.fail(
17360 block,
17361 src,
17362 "bit shifting operation expected integer type, found '{f}'",
17363 .{operand.fmt(pt)},
17364 );
17365}
17366
17367fn zirTypeofPeer(
17368 sema: *Sema,
17369 block: *Block,
17370 extended: Zir.Inst.Extended.InstData,
17371 inst: Zir.Inst.Index,
17372) CompileError!Air.Inst.Ref {
17373 const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
17374 const src = block.nodeOffset(extra.data.src_node);
17375 const body = sema.code.bodySlice(extra.data.body_index, extra.data.body_len);
17376
17377 var child_block: Block = .{
17378 .parent = block,
17379 .sema = sema,
17380 .namespace = block.namespace,
17381 .instructions = .empty,
17382 .inlining = block.inlining,
17383 .comptime_reason = null,
17384 .is_typeof = true,
17385 .runtime_cond = block.runtime_cond,
17386 .runtime_loop = block.runtime_loop,
17387 .runtime_index = block.runtime_index,
17388 .src_base_inst = block.src_base_inst,
17389 .type_name_ctx = block.type_name_ctx,
17390 .type_fqn_ctx = block.type_fqn_ctx,
17391 };
17392 defer child_block.instructions.deinit(sema.gpa);
17393 // Ignore the result, we only care about the instructions in `args`.
17394 _ = try sema.analyzeInlineBody(&child_block, body, inst);
17395
17396 const args = sema.code.refSlice(extra.end, extended.small);
17397
17398 const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len);
17399 defer sema.gpa.free(inst_list);
17400
17401 for (args, 0..) |arg_ref, i| {
17402 inst_list[i] = sema.resolveInst(arg_ref);
17403 }
17404
17405 const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node });
17406 return Air.internedToRef(result_type.toIntern());
17407}
17408
17409fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17410 const pt = sema.pt;
17411 const zcu = pt.zcu;
17412 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
17413 const src = block.nodeOffset(inst_data.src_node);
17414 const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node });
17415 const uncasted_operand = sema.resolveInst(inst_data.operand);
17416 const uncasted_ty = sema.typeOf(uncasted_operand);
17417 if (uncasted_ty.isVector(zcu)) {
17418 if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) {
17419 return sema.fail(block, operand_src, "boolean not operation on type '{f}'", .{
17420 uncasted_ty.fmt(pt),
17421 });
17422 }
17423 return analyzeBitNot(sema, block, uncasted_operand, src);
17424 }
17425 const operand = try sema.coerce(block, .bool, uncasted_operand, operand_src);
17426 if (sema.resolveValue(operand)) |val| {
17427 return if (val.isUndef(zcu)) .undef_bool else if (val.toBool()) .bool_false else .bool_true;
17428 }
17429 try sema.requireRuntimeBlock(block, src, null);
17430 return block.addTyOp(.not, .bool, operand);
17431}
17432
17433fn zirBoolBr(
17434 sema: *Sema,
17435 parent_block: *Block,
17436 inst: Zir.Inst.Index,
17437 is_bool_or: bool,
17438) CompileError!Air.Inst.Ref {
17439 const pt = sema.pt;
17440 const zcu = pt.zcu;
17441 const gpa = sema.gpa;
17442
17443 const datas = sema.code.instructions.items(.data);
17444 const inst_data = datas[@backingInt(inst)].pl_node;
17445 const extra = sema.code.extraData(Zir.Inst.BoolBr, inst_data.payload_index);
17446
17447 const uncoerced_lhs = sema.resolveInst(extra.data.lhs);
17448 const body = sema.code.bodySlice(extra.end, extra.data.body_len);
17449 const lhs_src = parent_block.src(.{ .node_offset_bin_lhs = inst_data.src_node });
17450 const rhs_src = parent_block.src(.{ .node_offset_bin_rhs = inst_data.src_node });
17451
17452 const lhs = try sema.coerce(parent_block, .bool, uncoerced_lhs, lhs_src);
17453
17454 if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
17455 if (is_bool_or and lhs_val.toBool()) {
17456 return .bool_true;
17457 } else if (!is_bool_or and !lhs_val.toBool()) {
17458 return .bool_false;
17459 }
17460 // comptime-known left-hand side. No need for a block here; the result
17461 // is simply the rhs expression. Here we rely on there only being 1
17462 // break instruction (`break_inline`).
17463 const rhs_result = try sema.resolveInlineBody(parent_block, body, inst);
17464 if (sema.typeOf(rhs_result).isNoReturn(zcu)) {
17465 return rhs_result;
17466 }
17467 return sema.coerce(parent_block, .bool, rhs_result, rhs_src);
17468 }
17469
17470 const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
17471 try sema.air_instructions.append(gpa, .{
17472 .tag = .block,
17473 .data = .{ .ty_pl = .{
17474 .ty = .bool,
17475 .payload = undefined,
17476 } },
17477 });
17478
17479 var child_block = parent_block.makeSubBlock();
17480 child_block.runtime_loop = null;
17481 child_block.runtime_cond = lhs_src;
17482 child_block.runtime_index.increment();
17483 defer child_block.instructions.deinit(gpa);
17484
17485 var then_block = child_block.makeSubBlock();
17486 defer then_block.instructions.deinit(gpa);
17487
17488 var else_block = child_block.makeSubBlock();
17489 defer else_block.instructions.deinit(gpa);
17490
17491 const lhs_block = if (is_bool_or) &then_block else &else_block;
17492 const rhs_block = if (is_bool_or) &else_block else &then_block;
17493
17494 const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false;
17495 _ = try lhs_block.addBr(block_inst, lhs_result);
17496
17497 const parent_hint = sema.branch_hint;
17498 defer sema.branch_hint = parent_hint;
17499 sema.branch_hint = null;
17500
17501 const rhs_result = try sema.resolveInlineBody(rhs_block, body, inst);
17502 const rhs_noret = sema.typeOf(rhs_result).isNoReturn(zcu);
17503 const coerced_rhs_result = if (!rhs_noret) rhs: {
17504 const coerced_result = try sema.coerce(rhs_block, .bool, rhs_result, rhs_src);
17505 _ = try rhs_block.addBr(block_inst, coerced_result);
17506 break :rhs coerced_result;
17507 } else rhs_result;
17508
17509 const rhs_hint = sema.branch_hint orelse .none;
17510
17511 const result = try sema.finishCondBr(
17512 parent_block,
17513 &child_block,
17514 &then_block,
17515 &else_block,
17516 lhs,
17517 block_inst,
17518 if (is_bool_or) .{
17519 .true = .none,
17520 .false = rhs_hint,
17521 .then_cov = .poi,
17522 .else_cov = .poi,
17523 } else .{
17524 .true = rhs_hint,
17525 .false = .none,
17526 .then_cov = .poi,
17527 .else_cov = .poi,
17528 },
17529 );
17530 if (!rhs_noret) {
17531 if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| {
17532 if (is_bool_or and rhs_val.toBool()) {
17533 return .bool_true;
17534 } else if (!is_bool_or and !rhs_val.toBool()) {
17535 return .bool_false;
17536 }
17537 }
17538 }
17539
17540 return result;
17541}
17542
17543fn finishCondBr(
17544 sema: *Sema,
17545 parent_block: *Block,
17546 child_block: *Block,
17547 then_block: *Block,
17548 else_block: *Block,
17549 cond: Air.Inst.Ref,
17550 block_inst: Air.Inst.Index,
17551 branch_hints: Air.CondBr.BranchHints,
17552) !Air.Inst.Ref {
17553 const gpa = sema.gpa;
17554
17555 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".field_names.len +
17556 then_block.instructions.items.len + else_block.instructions.items.len +
17557 @typeInfo(Air.Block).@"struct".field_names.len + child_block.instructions.items.len + 1);
17558
17559 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
17560 .then_body_len = @intCast(then_block.instructions.items.len),
17561 .else_body_len = @intCast(else_block.instructions.items.len),
17562 .branch_hints = branch_hints,
17563 });
17564 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
17565 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
17566
17567 _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{
17568 .operand = cond,
17569 .payload = cond_br_payload,
17570 } } });
17571
17572 sema.air_instructions.items(.data)[@backingInt(block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(
17573 Air.Block{ .body_len = @intCast(child_block.instructions.items.len) },
17574 );
17575 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items));
17576
17577 try parent_block.instructions.append(gpa, block_inst);
17578 return block_inst.toRef();
17579}
17580
17581fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
17582 const pt = sema.pt;
17583 const zcu = pt.zcu;
17584 switch (ty.zigTypeTag(zcu)) {
17585 .optional, .null, .undefined => return,
17586 .pointer => if (ty.isPtrLikeOptional(zcu)) return,
17587 else => {},
17588 }
17589 return sema.failWithExpectedOptionalType(block, src, ty);
17590}
17591
17592fn checkSentinelType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
17593 const pt = sema.pt;
17594 const zcu = pt.zcu;
17595 if (!ty.isSelfComparable(zcu, true)) {
17596 return sema.fail(block, src, "non-scalar sentinel type '{f}'", .{ty.fmt(pt)});
17597 }
17598}
17599
17600fn zirIsNonNull(
17601 sema: *Sema,
17602 block: *Block,
17603 inst: Zir.Inst.Index,
17604) CompileError!Air.Inst.Ref {
17605 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
17606 const src = block.nodeOffset(inst_data.src_node);
17607 const operand = sema.resolveInst(inst_data.operand);
17608 try sema.checkNullableType(block, src, sema.typeOf(operand));
17609 return sema.analyzeIsNull(block, src, operand, true);
17610}
17611
17612fn zirIsNonNullPtr(
17613 sema: *Sema,
17614 block: *Block,
17615 inst: Zir.Inst.Index,
17616) CompileError!Air.Inst.Ref {
17617 const pt = sema.pt;
17618 const zcu = pt.zcu;
17619 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
17620 const src = block.nodeOffset(inst_data.src_node);
17621 const ptr = sema.resolveInst(inst_data.operand);
17622 const ptr_ty = sema.typeOf(ptr);
17623 assert(ptr_ty.zigTypeTag(zcu) == .pointer);
17624 const nullable_ty = ptr_ty.childType(zcu);
17625
17626 try sema.checkNullableType(block, src, nullable_ty);
17627 try sema.ensureLayoutResolved(nullable_ty, src, .ptr_access);
17628
17629 if (try sema.resolveIsNullFromType(block, src, nullable_ty)) |is_null| {
17630 return .fromValue(.makeBool(!is_null));
17631 }
17632
17633 if (sema.resolveValue(ptr)) |ptr_val| {
17634 if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |nullable_val| {
17635 return sema.analyzeIsNull(block, src, .fromValue(nullable_val), true);
17636 }
17637 }
17638
17639 return block.addUnOp(.is_non_null_ptr, ptr);
17640}
17641
17642fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
17643 const pt = sema.pt;
17644 const zcu = pt.zcu;
17645 switch (ty.zigTypeTag(zcu)) {
17646 .error_set, .error_union, .undefined => return,
17647 else => return sema.fail(block, src, "expected error union type, found '{f}'", .{
17648 ty.fmt(pt),
17649 }),
17650 }
17651}
17652
17653fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17654 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
17655 const src = block.nodeOffset(inst_data.src_node);
17656 const operand = sema.resolveInst(inst_data.operand);
17657 try sema.checkErrorType(block, src, sema.typeOf(operand));
17658 return sema.analyzeIsNonErr(block, src, operand);
17659}
17660
17661fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17662 const pt = sema.pt;
17663 const zcu = pt.zcu;
17664 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
17665 const src = block.nodeOffset(inst_data.src_node);
17666 const ptr = sema.resolveInst(inst_data.operand);
17667 const ptr_ty = sema.typeOf(ptr);
17668 assert(ptr_ty.zigTypeTag(zcu) == .pointer);
17669 const error_ty = ptr_ty.childType(zcu);
17670 try sema.checkErrorType(block, src, error_ty);
17671 const loaded = try sema.analyzeLoad(block, src, ptr, src);
17672 return sema.analyzeIsNonErr(block, src, loaded);
17673}
17674
17675fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17676 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
17677 const src = block.nodeOffset(inst_data.src_node);
17678 const operand = sema.resolveInst(inst_data.operand);
17679 return sema.analyzeIsNonErr(block, src, operand);
17680}
17681
17682fn zirCondbr(
17683 sema: *Sema,
17684 parent_block: *Block,
17685 inst: Zir.Inst.Index,
17686) CompileError!void {
17687 const pt = sema.pt;
17688 const zcu = pt.zcu;
17689 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
17690 const cond_src = parent_block.src(.{ .node_offset_if_cond = inst_data.src_node });
17691 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
17692
17693 const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
17694 const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
17695
17696 const uncasted_cond = sema.resolveInst(extra.data.condition);
17697 const cond = try sema.coerce(parent_block, .bool, uncasted_cond, cond_src);
17698
17699 if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| {
17700 const body = if (cond_val.toBool()) then_body else else_body;
17701
17702 // We can propagate `.cold` hints from this branch since it's comptime-known
17703 // to be taken from the parent branch.
17704 const parent_hint = sema.branch_hint;
17705 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
17706
17707 try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src);
17708 // We use `analyzeBodyInner` since we want to propagate any comptime control flow to the caller.
17709 return sema.analyzeBodyInner(parent_block, body);
17710 }
17711
17712 const gpa = sema.gpa;
17713
17714 // We'll re-use the sub block to save on memory bandwidth, and yank out the
17715 // instructions array in between using it for the then block and else block.
17716 var sub_block = parent_block.makeSubBlock();
17717 sub_block.runtime_loop = null;
17718 sub_block.runtime_cond = cond_src;
17719 sub_block.runtime_index.increment();
17720 sub_block.need_debug_scope = null; // this body is emitted regardless
17721 defer sub_block.instructions.deinit(gpa);
17722
17723 const true_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, then_body);
17724 const true_instructions = try sub_block.instructions.toOwnedSlice(gpa);
17725 defer gpa.free(true_instructions);
17726
17727 const err_cond = blk: {
17728 const index = extra.data.condition.toIndex() orelse break :blk null;
17729 if (sema.code.instructions.items(.tag)[@backingInt(index)] != .is_non_err) break :blk null;
17730
17731 const err_inst_data = sema.code.instructions.items(.data)[@backingInt(index)].un_node;
17732 const err_operand = sema.resolveInst(err_inst_data.operand);
17733 const operand_ty = sema.typeOf(err_operand);
17734 assert(operand_ty.zigTypeTag(zcu) == .error_union);
17735 const result_ty = operand_ty.errorUnionSet(zcu);
17736 break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand);
17737 };
17738
17739 // Reset, this may have been updated by the then block analysis
17740 sub_block.error_return_trace_index = parent_block.error_return_trace_index;
17741
17742 const false_hint: std.lang.BranchHint = if (err_cond != null and
17743 try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src, false))
17744 h: {
17745 // nothing to do here. weight against error branch
17746 break :h .unlikely;
17747 } else try sema.analyzeBodyRuntimeBreak(&sub_block, else_body);
17748
17749 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".field_names.len +
17750 true_instructions.len + sub_block.instructions.items.len);
17751 _ = try parent_block.addInst(.{
17752 .tag = .cond_br,
17753 .data = .{
17754 .pl_op = .{
17755 .operand = cond,
17756 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
17757 .then_body_len = @intCast(true_instructions.len),
17758 .else_body_len = @intCast(sub_block.instructions.items.len),
17759 .branch_hints = .{
17760 .true = true_hint,
17761 .false = false_hint,
17762 // Code coverage is desired for error handling.
17763 .then_cov = .poi,
17764 .else_cov = .poi,
17765 },
17766 }),
17767 },
17768 },
17769 });
17770 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
17771 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
17772}
17773
17774fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17775 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
17776 const src = parent_block.nodeOffset(inst_data.src_node);
17777 const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node });
17778 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
17779 const body = sema.code.bodySlice(extra.end, extra.data.body_len);
17780 const err_union = sema.resolveInst(extra.data.operand);
17781 const err_union_ty = sema.typeOf(err_union);
17782 const pt = sema.pt;
17783 const zcu = pt.zcu;
17784 if (err_union_ty.zigTypeTag(zcu) != .error_union) {
17785 return sema.failWithOwnedErrorMsg(parent_block, msg: {
17786 const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
17787 errdefer msg.destroy(sema.gpa);
17788 try sema.addDeclaredHereNote(msg, err_union_ty);
17789 try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
17790 break :msg msg;
17791 });
17792 }
17793 if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| {
17794 // We can propagate `.cold` hints from this branch since it's comptime-known
17795 // to be taken from the parent branch.
17796 const parent_hint = sema.branch_hint;
17797 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
17798
17799 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(parent_block, operand_src, null);
17800 if (is_non_err_val.toBool()) {
17801 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false);
17802 }
17803 // We can analyze the body directly in the parent block because we know there are
17804 // no breaks from the body possible, and that the body is noreturn.
17805 try sema.analyzeBodyInner(parent_block, body);
17806 return .unreachable_value;
17807 }
17808
17809 var sub_block = parent_block.makeSubBlock();
17810 defer sub_block.instructions.deinit(sema.gpa);
17811
17812 const parent_hint = sema.branch_hint;
17813 defer sema.branch_hint = parent_hint;
17814
17815 // This body is guaranteed to end with noreturn and has no breaks.
17816 try sema.analyzeBodyInner(&sub_block, body);
17817
17818 // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`.
17819 const is_cold = sema.branch_hint == .cold;
17820
17821 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).@"struct".field_names.len +
17822 sub_block.instructions.items.len);
17823 const try_inst = try parent_block.addInst(.{
17824 .tag = if (is_cold) .try_cold else .@"try",
17825 .data = .{ .pl_op = .{
17826 .operand = err_union,
17827 .payload = sema.addExtraAssumeCapacity(Air.Try{
17828 .body_len = @intCast(sub_block.instructions.items.len),
17829 }),
17830 } },
17831 });
17832 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
17833
17834 // The payload type might still be OPV, in which case `try_inst` is just there for the runtime
17835 // control flow and we should return a comptime-known result.
17836 if (try err_union_ty.errorUnionPayload(zcu).onePossibleValue(pt)) |opv| return .fromValue(opv);
17837
17838 return try_inst;
17839}
17840
17841fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
17842 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
17843 const src = parent_block.nodeOffset(inst_data.src_node);
17844 const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node });
17845 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
17846 const body = sema.code.bodySlice(extra.end, extra.data.body_len);
17847 const operand = sema.resolveInst(extra.data.operand);
17848 const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
17849 const err_union_ty = sema.typeOf(err_union);
17850 const pt = sema.pt;
17851 const zcu = pt.zcu;
17852 if (err_union_ty.zigTypeTag(zcu) != .error_union) {
17853 return sema.failWithOwnedErrorMsg(parent_block, msg: {
17854 const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)});
17855 errdefer msg.destroy(sema.gpa);
17856 try sema.addDeclaredHereNote(msg, err_union_ty);
17857 try sema.errNote(operand_src, msg, "consider omitting 'try'", .{});
17858 break :msg msg;
17859 });
17860 }
17861 if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| {
17862 // We can propagate `.cold` hints from this branch since it's comptime-known
17863 // to be taken from the parent branch.
17864 const parent_hint = sema.branch_hint;
17865 defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null;
17866
17867 if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(parent_block, operand_src, null);
17868 if (is_non_err_val.toBool()) {
17869 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
17870 }
17871 // We can analyze the body directly in the parent block because we know there are
17872 // no breaks from the body possible, and that the body is noreturn.
17873 try sema.analyzeBodyInner(parent_block, body);
17874 return .unreachable_value;
17875 }
17876
17877 var sub_block = parent_block.makeSubBlock();
17878 defer sub_block.instructions.deinit(sema.gpa);
17879
17880 const parent_hint = sema.branch_hint;
17881 defer sema.branch_hint = parent_hint;
17882
17883 // This body is guaranteed to end with noreturn and has no breaks.
17884 try sema.analyzeBodyInner(&sub_block, body);
17885
17886 // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`.
17887 const is_cold = sema.branch_hint == .cold;
17888
17889 const operand_ty = sema.typeOf(operand);
17890 const res_ty = try pt.ptrType(info: {
17891 var new = operand_ty.ptrInfo(zcu);
17892 new.child = err_union_ty.errorUnionPayload(zcu).toIntern();
17893 break :info new;
17894 });
17895 try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).@"struct".field_names.len +
17896 sub_block.instructions.items.len);
17897 const try_inst = try parent_block.addInst(.{
17898 .tag = if (is_cold) .try_ptr_cold else .try_ptr,
17899 .data = .{ .ty_pl = .{
17900 .ty = res_ty,
17901 .payload = sema.addExtraAssumeCapacity(Air.TryPtr{
17902 .ptr = operand,
17903 .body_len = @intCast(sub_block.instructions.items.len),
17904 }),
17905 } },
17906 });
17907 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
17908 return try_inst;
17909}
17910
17911fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*LabeledBlock {
17912 const gop = sema.inst_map.getOrPutAssumeCapacity(dest_block);
17913 if (gop.found_existing) existing: {
17914 // This may be a *result* from an earlier iteration of an inline loop.
17915 // In this case, there will not be a post-hoc block entry, and we can
17916 // continue with the logic below.
17917 const new_block_inst = gop.value_ptr.*.toIndex() orelse break :existing;
17918 return sema.post_hoc_blocks.get(new_block_inst) orelse break :existing;
17919 }
17920
17921 try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1);
17922
17923 const new_block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
17924 gop.value_ptr.* = new_block_inst.toRef();
17925 try sema.air_instructions.append(sema.gpa, .{
17926 .tag = .block,
17927 .data = undefined,
17928 });
17929 const labeled_block = try sema.gpa.create(LabeledBlock);
17930 labeled_block.* = .{
17931 .label = .{
17932 .zir_block = dest_block,
17933 .merges = .{
17934 .src_locs = .empty,
17935 .results = .empty,
17936 .br_list = .empty,
17937 .block_inst = new_block_inst,
17938 },
17939 },
17940 .block = .{
17941 .parent = block,
17942 .sema = sema,
17943 .namespace = block.namespace,
17944 .instructions = .empty,
17945 .label = &labeled_block.label,
17946 .inlining = block.inlining,
17947 .comptime_reason = block.comptime_reason,
17948 .src_base_inst = block.src_base_inst,
17949 .type_name_ctx = block.type_name_ctx,
17950 .type_fqn_ctx = block.type_fqn_ctx,
17951 },
17952 };
17953 sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block);
17954 return labeled_block;
17955}
17956
17957/// A `break` statement is inside a runtime condition, but trying to
17958/// break from an inline loop. In such case we must convert it to
17959/// a runtime break.
17960fn addRuntimeBreak(sema: *Sema, child_block: *Block, block_inst: Zir.Inst.Index, break_operand: Zir.Inst.Ref) !void {
17961 const labeled_block = try sema.ensurePostHoc(child_block, block_inst);
17962
17963 const operand = sema.resolveInst(break_operand);
17964 const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand);
17965
17966 try labeled_block.label.merges.results.append(sema.gpa, operand);
17967 try labeled_block.label.merges.br_list.append(sema.gpa, br_ref.toIndex().?);
17968 try labeled_block.label.merges.src_locs.append(sema.gpa, null);
17969
17970 labeled_block.block.runtime_index.increment();
17971 if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) {
17972 labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop;
17973 labeled_block.block.runtime_loop = child_block.runtime_loop;
17974 }
17975}
17976
17977fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
17978 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"unreachable";
17979 const src = block.nodeOffset(inst_data.src_node);
17980
17981 if (block.isComptime()) {
17982 return sema.fail(block, src, "reached unreachable code", .{});
17983 }
17984 // TODO Add compile error for @optimizeFor occurring too late in a scope.
17985 sema.analyzeUnreachable(block, src, true) catch |err| switch (err) {
17986 error.AlreadyReported => |e| {
17987 if (sema.err) |msg| {
17988 if (!mem.eql(u8, msg.msg, "runtime safety check not allowed in naked function")) return err;
17989 try sema.errNote(src, msg, "the end of a naked function is implicitly unreachable", .{});
17990 }
17991 return e;
17992 },
17993 else => |e| return e,
17994 };
17995}
17996
17997fn zirRetErrValue(
17998 sema: *Sema,
17999 block: *Block,
18000 inst: Zir.Inst.Index,
18001) CompileError!void {
18002 const pt = sema.pt;
18003 const zcu = pt.zcu;
18004 const comp = zcu.comp;
18005 const gpa = comp.gpa;
18006 const io = comp.io;
18007
18008 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok;
18009 const src = block.tokenOffset(inst_data.src_tok);
18010 const err_name = try zcu.intern_pool.getOrPutString(
18011 gpa,
18012 io,
18013 pt.tid,
18014 inst_data.get(sema.code),
18015 .no_embedded_nulls,
18016 );
18017 _ = try pt.getErrorValue(err_name);
18018 // Return the error code from the function.
18019 const error_set_type = try pt.singleErrorSetType(err_name);
18020 const result_inst = Air.internedToRef((try pt.intern(.{ .err = .{
18021 .ty = error_set_type.toIntern(),
18022 .name = err_name,
18023 } })));
18024 return sema.analyzeRet(block, result_inst, src, src);
18025}
18026
18027fn zirRetImplicit(
18028 sema: *Sema,
18029 block: *Block,
18030 inst: Zir.Inst.Index,
18031) CompileError!void {
18032 const pt = sema.pt;
18033 const zcu = pt.zcu;
18034 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_tok;
18035 const r_brace_src = block.tokenOffset(inst_data.src_tok);
18036 if (block.inlining == null and sema.func_is_naked) {
18037 assert(!block.isComptime());
18038 if (block.wantSafety()) {
18039 // Calling a safety function from a naked function would not be legal.
18040 _ = try block.addNoOp(.trap);
18041 } else {
18042 try sema.analyzeUnreachable(block, r_brace_src, false);
18043 }
18044 return;
18045 }
18046
18047 const operand = sema.resolveInst(inst_data.operand);
18048 const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = .zero });
18049 const base_tag = sema.fn_ret_ty.optEuBaseType(zcu).zigTypeTag(zcu);
18050 if (base_tag == .noreturn) {
18051 const msg = msg: {
18052 const msg = try sema.errMsg(ret_ty_src, "function declared '{f}' implicitly returns", .{
18053 sema.fn_ret_ty.fmt(pt),
18054 });
18055 errdefer msg.destroy(sema.gpa);
18056 try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{});
18057 break :msg msg;
18058 };
18059 return sema.failWithOwnedErrorMsg(block, msg);
18060 } else if (base_tag != .void) {
18061 const msg = msg: {
18062 const msg = try sema.errMsg(ret_ty_src, "function with non-void return type '{f}' implicitly returns", .{
18063 sema.fn_ret_ty.fmt(pt),
18064 });
18065 errdefer msg.destroy(sema.gpa);
18066 try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{});
18067 break :msg msg;
18068 };
18069 return sema.failWithOwnedErrorMsg(block, msg);
18070 }
18071
18072 return sema.analyzeRet(block, operand, r_brace_src, r_brace_src);
18073}
18074
18075fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
18076 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
18077 const operand = sema.resolveInst(inst_data.operand);
18078 const src = block.nodeOffset(inst_data.src_node);
18079
18080 return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node }));
18081}
18082
18083fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
18084 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
18085 const src = block.nodeOffset(inst_data.src_node);
18086 const ret_ptr = sema.resolveInst(inst_data.operand);
18087
18088 if (block.isComptime() or block.inlining != null or sema.func_is_naked) {
18089 const operand = try sema.analyzeLoad(block, src, ret_ptr, src);
18090 return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node }));
18091 }
18092
18093 if (sema.wantErrorReturnTracing()) {
18094 const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr);
18095 try sema.maybePushErrorTrace(block, src, is_non_err);
18096 }
18097
18098 _ = try block.addUnOp(.ret_load, ret_ptr);
18099}
18100
18101fn maybePushErrorTrace(
18102 sema: *Sema,
18103 parent_block: *Block,
18104 src: LazySrcLoc,
18105 is_non_err: Air.Inst.Ref,
18106) CompileError!void {
18107 const pt = sema.pt;
18108
18109 const need_check = switch (is_non_err) {
18110 .bool_true => return,
18111 .bool_false => false,
18112 else => true,
18113 };
18114
18115 // This means we're returning something that might be an error!
18116 // This should only be possible with the `auto` cc, so we definitely have an error trace.
18117 assert(pt.zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace);
18118
18119 const gpa = sema.gpa;
18120 const return_err_fn = Air.internedToRef(try sema.getStdLangValue(src, .returnError));
18121
18122 if (!need_check) {
18123 try sema.callBuiltin(parent_block, src, return_err_fn, .never_tail, &.{}, .@"error return");
18124 return;
18125 }
18126
18127 var err_block = parent_block.makeSubBlock();
18128 defer err_block.instructions.deinit(gpa);
18129 try sema.callBuiltin(&err_block, src, return_err_fn, .never_tail, &.{}, .@"error return");
18130
18131 try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
18132
18133 try sema.air_instructions.ensureUnusedCapacity(gpa, 4);
18134 try sema.air_extra.ensureUnusedCapacity(
18135 gpa,
18136 @typeInfo(Air.Block).@"struct".field_names.len +
18137 1 + // the main block contains only the `cond_br`
18138 @typeInfo(Air.CondBr).@"struct".field_names.len +
18139 1 + // the non-error branch contains only a `br`
18140 err_block.instructions.items.len + 1, // the error branch contains the `returnError` call and a `br`
18141 );
18142
18143 const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
18144 const cond_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len + 1));
18145 const then_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len + 2));
18146 const else_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len + 3));
18147
18148 const block_payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 });
18149 sema.air_extra.appendAssumeCapacity(@backingInt(cond_br_inst));
18150 sema.air_instructions.appendAssumeCapacity(.{
18151 .tag = .block,
18152 .data = .{ .ty_pl = .{
18153 .ty = .void,
18154 .payload = block_payload,
18155 } },
18156 });
18157
18158 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
18159 .then_body_len = 1,
18160 .else_body_len = @intCast(err_block.instructions.items.len + 1),
18161 .branch_hints = .{
18162 // Weight against error branch.
18163 .true = .likely,
18164 .false = .unlikely,
18165 // Code coverage is not valuable on either branch.
18166 .then_cov = .none,
18167 .else_cov = .none,
18168 },
18169 });
18170 sema.air_extra.appendAssumeCapacity(@backingInt(then_br_inst));
18171 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(err_block.instructions.items));
18172 sema.air_extra.appendAssumeCapacity(@backingInt(else_br_inst));
18173 sema.air_instructions.appendAssumeCapacity(.{
18174 .tag = .cond_br,
18175 .data = .{ .pl_op = .{
18176 .operand = is_non_err,
18177 .payload = cond_br_payload,
18178 } },
18179 });
18180
18181 const br_inst_data: Air.Inst = .{
18182 .tag = .br,
18183 .data = .{ .br = .{
18184 .block_inst = block_inst,
18185 .operand = .void_value,
18186 } },
18187 };
18188 sema.air_instructions.appendAssumeCapacity(br_inst_data); // then_br_inst
18189 sema.air_instructions.appendAssumeCapacity(br_inst_data); // else_br_inst
18190
18191 parent_block.instructions.appendAssumeCapacity(block_inst);
18192}
18193
18194fn wantErrorReturnTracing(sema: *Sema) bool {
18195 const zcu = sema.pt.zcu;
18196 return sema.fn_ret_ty.isError(zcu) and zcu.comp.config.any_error_tracing;
18197}
18198
18199fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
18200 const pt = sema.pt;
18201 const zcu = pt.zcu;
18202 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].save_err_ret_index;
18203
18204 if (!block.ownerModule().error_tracing) return;
18205
18206 // This is only relevant at runtime.
18207 if (block.isComptime() or block.is_typeof) return;
18208
18209 const save_index = inst_data.operand == .none or b: {
18210 const operand = sema.resolveInst(inst_data.operand);
18211 const operand_ty = sema.typeOf(operand);
18212 break :b operand_ty.isError(zcu);
18213 };
18214
18215 if (save_index)
18216 block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block);
18217}
18218
18219fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
18220 const extra = sema.code.extraData(Zir.Inst.RestoreErrRetIndex, extended.operand).data;
18221 return sema.restoreErrRetIndex(start_block, start_block.nodeOffset(extra.src_node), extra.block, extra.operand);
18222}
18223
18224/// If `operand` is non-error (or is `none`), restores the error return trace to
18225/// its state at the point `block` was reached (or, if `block` is `none`, the
18226/// point this function began execution).
18227fn restoreErrRetIndex(sema: *Sema, start_block: *Block, src: LazySrcLoc, target_block: Zir.Inst.Ref, operand_zir: Zir.Inst.Ref) CompileError!void {
18228 const pt = sema.pt;
18229 const zcu = pt.zcu;
18230
18231 const saved_index = if (target_block.toIndexAllowNone()) |zir_block| b: {
18232 var block = start_block;
18233 while (true) {
18234 if (block.label) |label| {
18235 if (label.zir_block == zir_block) {
18236 const target_trace_index = if (block.parent) |parent_block|
18237 parent_block.error_return_trace_index
18238 else
18239 sema.error_return_trace_index_on_fn_entry;
18240
18241 if (start_block.error_return_trace_index != target_trace_index)
18242 break :b target_trace_index;
18243
18244 return; // No need to restore
18245 }
18246 }
18247 block = block.parent.?;
18248 }
18249 } else b: {
18250 if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry)
18251 break :b sema.error_return_trace_index_on_fn_entry;
18252
18253 return; // No need to restore
18254 };
18255
18256 const operand = sema.resolveInstAllowNone(operand_zir);
18257
18258 if (start_block.isComptime() or start_block.is_typeof) {
18259 const is_non_error = if (operand != .none) blk: {
18260 const is_non_error_inst = try sema.analyzeIsNonErr(start_block, src, operand);
18261 const cond_val = try sema.resolveDefinedValue(start_block, src, is_non_error_inst);
18262 break :blk cond_val.?.toBool();
18263 } else true; // no operand means pop unconditionally
18264
18265 if (is_non_error) return;
18266
18267 const saved_index_val = try sema.resolveDefinedValue(start_block, src, saved_index);
18268 const saved_index_int = saved_index_val.?.toUnsignedInt(zcu);
18269 assert(saved_index_int <= sema.comptime_err_ret_trace.items.len);
18270 sema.comptime_err_ret_trace.items.len = @intCast(saved_index_int);
18271 return;
18272 }
18273
18274 if (!zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace) return;
18275 if (!start_block.ownerModule().error_tracing) return;
18276
18277 assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere
18278
18279 return sema.popErrorReturnTrace(start_block, src, operand, saved_index);
18280}
18281
18282fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void {
18283 const pt = sema.pt;
18284 const zcu = pt.zcu;
18285 const ip = &zcu.intern_pool;
18286 assert(sema.fn_ret_ty.zigTypeTag(zcu) == .error_union);
18287 const err_set_ty = sema.fn_ret_ty.errorUnionSet(zcu).toIntern();
18288 switch (err_set_ty) {
18289 .adhoc_inferred_error_set_type => {
18290 const ies = sema.fn_ret_ty_ies.?;
18291 assert(ies.func == .none);
18292 try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand));
18293 },
18294 else => if (ip.isInferredErrorSetType(err_set_ty)) {
18295 const ies = sema.fn_ret_ty_ies.?;
18296 assert(ies.func == sema.owner.unwrap().func);
18297 try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand));
18298 },
18299 }
18300}
18301
18302fn addToInferredErrorSetPtr(sema: *Sema, ies: *InferredErrorSet, op_ty: Type) !void {
18303 const arena = sema.arena;
18304 const pt = sema.pt;
18305 const zcu = pt.zcu;
18306 const ip = &zcu.intern_pool;
18307 switch (op_ty.zigTypeTag(zcu)) {
18308 .error_set => try ies.addErrorSet(op_ty, ip, arena),
18309 .error_union => try ies.addErrorSet(op_ty.errorUnionSet(zcu), ip, arena),
18310 else => {},
18311 }
18312}
18313
18314fn analyzeRet(
18315 sema: *Sema,
18316 block: *Block,
18317 uncasted_operand: Air.Inst.Ref,
18318 src: LazySrcLoc,
18319 operand_src: LazySrcLoc,
18320) CompileError!void {
18321 // Special case for returning an error to an inferred error set; we need to
18322 // add the error tag to the inferred error set of the in-scope function, so
18323 // that the coercion below works correctly.
18324 const pt = sema.pt;
18325 const zcu = pt.zcu;
18326 if (sema.fn_ret_ty_ies != null and sema.fn_ret_ty.zigTypeTag(zcu) == .error_union) {
18327 try sema.addToInferredErrorSet(uncasted_operand);
18328 }
18329 const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, operand_src, .{ .is_ret = true }) catch |err| switch (err) {
18330 error.NotCoercible => unreachable,
18331 else => |e| return e,
18332 };
18333
18334 if (block.isComptime()) {
18335 const inlining = block.inlining orelse {
18336 return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{});
18337 };
18338 assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr
18339 const ret_val = try sema.resolveConstValue(block, operand_src, operand, null);
18340 inlining.comptime_result = operand;
18341
18342 if (sema.fn_ret_ty.isError(zcu) and ret_val.getErrorName(zcu) != .none) {
18343 try sema.comptime_err_ret_trace.append(src);
18344 }
18345 return error.ComptimeReturn;
18346 }
18347
18348 if (block.inlining == null and sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: {
18349 const msg = try sema.errMsg(src, "cannot return from naked function", .{});
18350 errdefer msg.destroy(sema.gpa);
18351
18352 try sema.errNote(src, msg, "can only return using assembly", .{});
18353 break :msg msg;
18354 });
18355
18356 if (sema.wantErrorReturnTracing()) {
18357 const is_non_err = try sema.analyzeIsNonErr(block, operand_src, operand);
18358 try sema.maybePushErrorTrace(block, src, is_non_err);
18359 }
18360
18361 if (block.inlining) |inlining| {
18362 assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr
18363 const br_inst = try block.addBr(inlining.merges.block_inst, operand);
18364 try inlining.merges.results.append(sema.gpa, operand);
18365 try inlining.merges.br_list.append(sema.gpa, br_inst.toIndex().?);
18366 try inlining.merges.src_locs.append(sema.gpa, operand_src);
18367 var body_block = block;
18368 while (body_block.parent) |parent| body_block = parent;
18369 if (body_block.runtime_cond == null and body_block.runtime_loop == null) {
18370 body_block.runtime_cond = block.runtime_cond orelse block.runtime_loop;
18371 body_block.runtime_loop = block.runtime_loop;
18372 }
18373 } else {
18374 try sema.validateRuntimeValue(block, operand_src, operand);
18375 const ret_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret;
18376 _ = try block.addUnOp(ret_tag, operand);
18377 }
18378}
18379
18380fn floatOpAllowed(tag: Zir.Inst.Tag) bool {
18381 // extend this swich as additional operators are implemented
18382 return switch (tag) {
18383 .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .div_ceil, .mod, .rem, .mod_rem => true,
18384 else => false,
18385 };
18386}
18387
18388fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
18389 const pt = sema.pt;
18390 const zcu = pt.zcu;
18391 const comp = zcu.comp;
18392 const gpa = comp.gpa;
18393 const io = comp.io;
18394 const ip = &zcu.intern_pool;
18395
18396 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].ptr_type;
18397 const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index);
18398 const elem_ty_src = block.src(.{ .node_offset_ptr_elem = extra.data.src_node });
18399 const sentinel_src = block.src(.{ .node_offset_ptr_sentinel = extra.data.src_node });
18400 const align_src = block.src(.{ .node_offset_ptr_align = extra.data.src_node });
18401 const addrspace_src = block.src(.{ .node_offset_ptr_addrspace = extra.data.src_node });
18402 const bitoffset_src = block.src(.{ .node_offset_ptr_bitoffset = extra.data.src_node });
18403 const hostsize_src = block.src(.{ .node_offset_ptr_hostsize = extra.data.src_node });
18404
18405 const elem_ty = blk: {
18406 const air_inst = sema.resolveInst(extra.data.elem_type);
18407 const ty = sema.analyzeAsType(block, elem_ty_src, .type, air_inst) catch |err| switch (err) {
18408 error.AlreadyReported => |e| {
18409 if (sema.err) |msg| {
18410 if (sema.typeOf(air_inst).isSinglePointer(zcu)) {
18411 try sema.errNote(elem_ty_src, msg, "use '.*' to dereference pointer", .{});
18412 }
18413 }
18414 return e;
18415 },
18416 else => |e| return e,
18417 };
18418 assert(!ty.isGenericPoison());
18419 break :blk ty;
18420 };
18421
18422 if (elem_ty.zigTypeTag(zcu) == .noreturn)
18423 return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{});
18424
18425 const target = zcu.getTarget();
18426
18427 var extra_i = extra.end;
18428
18429 const sentinel = if (inst_data.flags.has_sentinel) blk: {
18430 const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i]));
18431 extra_i += 1;
18432 const coerced = try sema.coerce(block, elem_ty, sema.resolveInst(ref), sentinel_src);
18433 const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{ .simple = .pointer_sentinel });
18434 try checkSentinelType(sema, block, sentinel_src, elem_ty);
18435 if (val.canMutateComptimeVarState(zcu)) {
18436 const sentinel_name = try ip.getOrPutString(gpa, io, pt.tid, "sentinel", .no_embedded_nulls);
18437 return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", val);
18438 }
18439 break :blk val.toIntern();
18440 } else .none;
18441
18442 const abi_align: Alignment = if (inst_data.flags.has_align) blk: {
18443 const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i]));
18444 extra_i += 1;
18445 const coerced = try sema.coerce(block, align_ty, sema.resolveInst(ref), align_src);
18446 const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" });
18447 const align_bytes = val.toUnsignedInt(zcu);
18448 break :blk try sema.validateAlign(block, align_src, align_bytes);
18449 } else .none;
18450
18451 const address_space: std.lang.AddressSpace = if (inst_data.flags.has_addrspace) blk: {
18452 const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i]));
18453 extra_i += 1;
18454 break :blk try sema.resolveAddressSpace(block, addrspace_src, ref, .pointer);
18455 } else if (elem_ty.zigTypeTag(zcu) == .@"fn" and target.cpu.arch == .avr) .flash else .generic;
18456
18457 const bit_offset: u16 = if (inst_data.flags.has_bit_range) blk: {
18458 const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i]));
18459 extra_i += 1;
18460 const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, .u16, .{ .simple = .type });
18461 break :blk @intCast(bit_offset);
18462 } else 0;
18463
18464 const host_size: u16 = if (inst_data.flags.has_bit_range) blk: {
18465 const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i]));
18466 extra_i += 1;
18467 const host_size = try sema.resolveInt(block, hostsize_src, ref, .u16, .{ .simple = .type });
18468 break :blk @intCast(host_size);
18469 } else 0;
18470
18471 if (host_size != 0) {
18472 try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .bit_ptr_child);
18473 if (elem_ty.unpackable(zcu)) |reason| return sema.failWithOwnedErrorMsg(block, msg: {
18474 const msg = try sema.errMsg(elem_ty_src, "bit-pointer cannot refer to value of type '{f}'", .{elem_ty.fmt(pt)});
18475 errdefer msg.destroy(sema.gpa);
18476 try sema.explainWhyTypeIsUnpackable(msg, elem_ty_src, reason);
18477 break :msg msg;
18478 });
18479 const elem_bit_size = elem_ty.bitSize(zcu);
18480 if (bit_offset >= host_size * 8) {
18481 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", .{
18482 elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size,
18483 });
18484 }
18485 if (elem_bit_size > host_size * 8 - bit_offset) {
18486 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", .{
18487 elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size,
18488 });
18489 }
18490 }
18491
18492 if (elem_ty.zigTypeTag(zcu) == .@"fn") {
18493 if (inst_data.size != .one) {
18494 return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{});
18495 }
18496 } else if (inst_data.size != .one and elem_ty.zigTypeTag(zcu) == .@"opaque") {
18497 return sema.fail(block, elem_ty_src, "indexable pointer to opaque type '{f}' not allowed", .{elem_ty.fmt(pt)});
18498 }
18499
18500 const ty = try pt.ptrType(.{
18501 .child = elem_ty.toIntern(),
18502 .sentinel = sentinel,
18503 .flags = .{
18504 .alignment = abi_align,
18505 .address_space = address_space,
18506 .is_const = !inst_data.flags.is_mutable,
18507 .is_allowzero = inst_data.flags.is_allowzero,
18508 .is_volatile = inst_data.flags.is_volatile,
18509 .size = inst_data.size,
18510 },
18511 .packed_offset = .{
18512 .bit_offset = bit_offset,
18513 .host_size = host_size,
18514 },
18515 });
18516 return Air.internedToRef(ty.toIntern());
18517}
18518
18519fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
18520 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
18521 const src = block.nodeOffset(inst_data.src_node);
18522 const ty_src = block.src(.{ .node_offset_init_ty = inst_data.src_node });
18523 const obj_ty = try sema.resolveType(block, ty_src, inst_data.operand);
18524 const pt = sema.pt;
18525 const zcu = pt.zcu;
18526
18527 try sema.ensureLayoutResolved(obj_ty, ty_src, .init);
18528
18529 switch (obj_ty.zigTypeTag(zcu)) {
18530 .@"struct" => return sema.structInitEmpty(block, obj_ty, src, src),
18531 .array, .vector => return sema.arrayInitEmpty(block, src, obj_ty),
18532 .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}),
18533 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
18534 }
18535}
18536
18537fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_byref: bool) CompileError!Air.Inst.Ref {
18538 const pt = sema.pt;
18539 const zcu = pt.zcu;
18540 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
18541 const src = block.nodeOffset(inst_data.src_node);
18542
18543 // Generic poison means this is an untyped anonymous empty struct/array init
18544 const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse {
18545 if (is_byref) {
18546 return sema.uavRef(.empty_tuple);
18547 } else {
18548 return .empty_tuple;
18549 }
18550 };
18551
18552 const init_ty = if (is_byref) ty: {
18553 const ptr_ty = ty_operand.optEuBaseType(zcu);
18554 assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction
18555 switch (ptr_ty.ptrSize(zcu)) {
18556 // Use a zero-length array for a slice or many-ptr result
18557 .slice, .many => break :ty try pt.arrayType(.{
18558 .len = 0,
18559 .child = ptr_ty.childType(zcu).toIntern(),
18560 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
18561 }),
18562 // Just use the child type for a single-pointer or C-pointer result
18563 .one, .c => {
18564 const child = ptr_ty.childType(zcu);
18565 if (child.toIntern() == .anyopaque_type) {
18566 // ...unless that child is anyopaque, in which case this is equivalent to an untyped init.
18567 // `.{}` is an empty tuple.
18568 if (is_byref) {
18569 return sema.uavRef(.empty_tuple);
18570 } else {
18571 return .empty_tuple;
18572 }
18573 }
18574 break :ty child;
18575 },
18576 }
18577 if (!ptr_ty.isSlice(zcu)) {
18578 break :ty ptr_ty.childType(zcu);
18579 }
18580 // To make `&.{}` a `[:s]T`, the init should be a `[0:s]T`.
18581 break :ty try pt.arrayType(.{
18582 .len = 0,
18583 .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
18584 .child = ptr_ty.childType(zcu).toIntern(),
18585 });
18586 } else ty_operand;
18587
18588 try sema.ensureLayoutResolved(init_ty, src, .init);
18589
18590 const obj_ty = init_ty.optEuBaseType(zcu);
18591
18592 const empty_ref = switch (obj_ty.zigTypeTag(zcu)) {
18593 .@"struct" => try sema.structInitEmpty(block, obj_ty, src, src),
18594 .array, .vector => try sema.arrayInitEmpty(block, src, obj_ty),
18595 .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}),
18596 else => return sema.failWithArrayInitNotSupported(block, src, obj_ty),
18597 };
18598 const init_ref = try sema.coerce(block, init_ty, empty_ref, src);
18599
18600 if (is_byref) {
18601 return sema.uavRef(sema.resolveValue(init_ref).?);
18602 } else {
18603 return init_ref;
18604 }
18605}
18606
18607/// Asserts that the layout of `struct_ty` is already resolved.
18608fn structInitEmpty(
18609 sema: *Sema,
18610 block: *Block,
18611 struct_ty: Type,
18612 dest_src: LazySrcLoc,
18613 init_src: LazySrcLoc,
18614) CompileError!Air.Inst.Ref {
18615 const pt = sema.pt;
18616 const zcu = pt.zcu;
18617 const gpa = sema.gpa;
18618 // This logic must be synchronized with that in `zirStructInit`.
18619 struct_ty.assertHasLayout(zcu);
18620
18621 // The init values to use for the struct instance.
18622 const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(zcu));
18623 defer gpa.free(field_inits);
18624 @memset(field_inits, .none);
18625
18626 // Maps field index in the struct declaration to the field index in the initialization expression.
18627 const field_assign_idxs = try gpa.alloc(?usize, struct_ty.structFieldCount(zcu));
18628 defer gpa.free(field_assign_idxs);
18629 @memset(field_assign_idxs, null);
18630
18631 return sema.finishStructInit(block, init_src, dest_src, field_inits, field_assign_idxs, struct_ty, struct_ty, false);
18632}
18633
18634fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref {
18635 const pt = sema.pt;
18636 const zcu = pt.zcu;
18637 const arr_len = obj_ty.arrayLen(zcu);
18638 if (arr_len != 0) {
18639 if (obj_ty.zigTypeTag(zcu) == .array) {
18640 return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len});
18641 } else {
18642 return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len});
18643 }
18644 }
18645 return .fromValue(try pt.aggregateValue(obj_ty, &.{}));
18646}
18647
18648fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
18649 const pt = sema.pt;
18650 const zcu = pt.zcu;
18651 const ip = &zcu.intern_pool;
18652 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
18653 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
18654 const field_src = block.builtinCallArgSrc(inst_data.src_node, 1);
18655 const payload_src = block.builtinCallArgSrc(inst_data.src_node, 2);
18656 const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
18657 const union_ty = try sema.resolveType(block, ty_src, extra.union_type);
18658 if (union_ty.zigTypeTag(pt.zcu) != .@"union") {
18659 return sema.fail(block, ty_src, "expected union type, found '{f}'", .{union_ty.fmt(pt)});
18660 }
18661 union_ty.assertHasLayout(zcu); // from a previous `field_type_ref` instruction
18662 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .union_field_names });
18663 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
18664 const field_ty: Type = .fromInterned(zcu.typeToUnion(union_ty).?.field_types.get(ip)[field_index]);
18665
18666 const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src);
18667
18668 if (union_ty.containerLayout(zcu) == .@"packed") {
18669 return sema.bitCastUnchecked(block, union_ty, payload);
18670 }
18671
18672 if (sema.resolveValue(payload)) |payload_val| {
18673 const tag_ty = union_ty.unionTagTypeHypothetical(zcu);
18674 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
18675 return .fromValue(try pt.unionValue(union_ty, tag_val, payload_val));
18676 }
18677
18678 try sema.requireRuntimeBlock(block, payload_src, null);
18679 return block.addUnionInit(union_ty, field_index, payload);
18680}
18681
18682fn zirStructInit(
18683 sema: *Sema,
18684 block: *Block,
18685 inst: Zir.Inst.Index,
18686 is_ref: bool,
18687) CompileError!Air.Inst.Ref {
18688 const pt = sema.pt;
18689 const zcu = pt.zcu;
18690 const comp = zcu.comp;
18691 const gpa = comp.gpa;
18692 const io = comp.io;
18693 const ip = &zcu.intern_pool;
18694
18695 const zir_datas = sema.code.instructions.items(.data);
18696 const inst_data = zir_datas[@backingInt(inst)].pl_node;
18697 const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
18698 const src = block.nodeOffset(inst_data.src_node);
18699
18700 const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
18701 const first_field_type_data = zir_datas[@backingInt(first_item.field_type)].pl_node;
18702 const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
18703 const result_ty = try sema.resolveTypeOrPoison(block, src, first_field_type_extra.container_type) orelse {
18704 // The type wasn't actually known, so treat this as an anon struct init.
18705 return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref);
18706 };
18707 try sema.ensureLayoutResolved(result_ty, src, .init);
18708 const resolved_ty = result_ty.optEuBaseType(zcu);
18709
18710 if (resolved_ty.zigTypeTag(zcu) == .@"struct") {
18711 // This logic must be synchronized with that in `zirStructInitEmpty`.
18712
18713 // Maps field index to field_type index of where it was already initialized.
18714 // For making sure all fields are accounted for and no fields are duplicated.
18715 const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(zcu));
18716 defer gpa.free(found_fields);
18717
18718 // The init values to use for the struct instance.
18719 const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(zcu));
18720 defer gpa.free(field_inits);
18721 @memset(field_inits, .none);
18722
18723 // Maps field index in the struct declaration to the field index in the initialization expression.
18724 const field_assign_idxs = try gpa.alloc(?usize, resolved_ty.structFieldCount(zcu));
18725 defer gpa.free(field_assign_idxs);
18726 @memset(field_assign_idxs, null);
18727
18728 var field_i: u32 = 0;
18729 var extra_index = extra.end;
18730
18731 while (field_i < extra.data.fields_len) : (field_i += 1) {
18732 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
18733 extra_index = item.end;
18734
18735 const field_type_data = zir_datas[@backingInt(item.data.field_type)].pl_node;
18736 const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node });
18737 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
18738 const field_name = try ip.getOrPutString(
18739 gpa,
18740 io,
18741 pt.tid,
18742 sema.code.nullTerminatedString(field_type_extra.name_start),
18743 .no_embedded_nulls,
18744 );
18745 const field_index = if (resolved_ty.isTuple(zcu))
18746 try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src)
18747 else
18748 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
18749 assert(field_inits[field_index] == .none);
18750 field_assign_idxs[field_index] = field_i;
18751 found_fields[field_index] = item.data.field_type;
18752 const uncoerced_init = sema.resolveInst(item.data.init);
18753 const field_ty = resolved_ty.fieldType(field_index, zcu);
18754 field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src);
18755 if (resolved_ty.structFieldIsComptime(field_index, zcu)) {
18756 const default_value = (try resolved_ty.structFieldValueComptime(pt, field_index)).?;
18757 const init_val = sema.resolveValue(field_inits[field_index]) orelse {
18758 return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field });
18759 };
18760 if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) {
18761 return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index);
18762 }
18763 }
18764 }
18765
18766 return sema.finishStructInit(block, src, src, field_inits, field_assign_idxs, resolved_ty, result_ty, is_ref);
18767 } else if (resolved_ty.zigTypeTag(zcu) == .@"union") {
18768 if (extra.data.fields_len != 1) {
18769 return sema.fail(block, src, "union initialization expects exactly one field", .{});
18770 }
18771
18772 const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end);
18773
18774 const field_type_data = zir_datas[@backingInt(item.data.field_type)].pl_node;
18775 const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node });
18776 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
18777 const field_name = try ip.getOrPutString(
18778 gpa,
18779 io,
18780 pt.tid,
18781 sema.code.nullTerminatedString(field_type_extra.name_start),
18782 .no_embedded_nulls,
18783 );
18784 const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src);
18785 const tag_ty = resolved_ty.unionTagTypeHypothetical(zcu);
18786 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index);
18787 const field_ty: Type = .fromInterned(zcu.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index]);
18788
18789 if (field_ty.classify(zcu) == .no_possible_value) {
18790 return sema.failWithOwnedErrorMsg(block, msg: {
18791 const msg = try sema.errMsg(src, "cannot initialize union field with uninstantiable type '{f}'", .{field_ty.fmt(pt)});
18792 errdefer msg.destroy(sema.gpa);
18793
18794 try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{f}' declared here", .{
18795 field_name.fmt(ip),
18796 });
18797 try sema.addDeclaredHereNote(msg, resolved_ty);
18798 break :msg msg;
18799 });
18800 }
18801
18802 const uncoerced_init_inst = sema.resolveInst(item.data.init);
18803 const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src);
18804
18805 if (resolved_ty.containerLayout(zcu) == .@"packed") {
18806 const union_val = try sema.bitCastUnchecked(block, resolved_ty, init_inst);
18807 const result_val = try sema.coerce(block, result_ty, union_val, src);
18808 if (is_ref) {
18809 return sema.analyzeRef(block, src, result_val, .none);
18810 } else {
18811 return result_val;
18812 }
18813 }
18814
18815 if (sema.resolveValue(init_inst)) |val| {
18816 const struct_val = Value.fromInterned(try pt.internUnion(.{
18817 .ty = resolved_ty.toIntern(),
18818 .tag = tag_val.toIntern(),
18819 .val = val.toIntern(),
18820 }));
18821 const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src);
18822 const final_val = sema.resolveValue(final_val_inst).?;
18823 return sema.addConstantMaybeRef(final_val, is_ref);
18824 }
18825
18826 if (resolved_ty.comptimeOnly(zcu)) {
18827 return sema.failWithNeededComptime(block, field_src, .{ .comptime_only = .{
18828 .ty = resolved_ty,
18829 .msg = .union_init,
18830 } });
18831 }
18832
18833 try sema.validateRuntimeValue(block, field_src, init_inst);
18834
18835 if (is_ref) {
18836 const target = zcu.getTarget();
18837 const alloc_ty = try pt.ptrType(.{
18838 .child = result_ty.toIntern(),
18839 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
18840 });
18841 const alloc = try block.addTy(.alloc, alloc_ty);
18842 const base_ptr = try sema.optEuBasePtrInit(block, alloc, src);
18843 const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true);
18844 try sema.storePtr(block, src, field_ptr, init_inst);
18845 return sema.makePtrConst(block, alloc);
18846 }
18847
18848 try sema.requireRuntimeBlock(block, src, null);
18849 const union_val = try block.addUnionInit(resolved_ty, field_index, init_inst);
18850 return sema.coerce(block, result_ty, union_val, src);
18851 }
18852 unreachable;
18853}
18854
18855fn finishStructInit(
18856 sema: *Sema,
18857 block: *Block,
18858 init_src: LazySrcLoc,
18859 dest_src: LazySrcLoc,
18860 field_inits: []Air.Inst.Ref,
18861 field_assign_idxs: []?usize,
18862 struct_ty: Type,
18863 result_ty: Type,
18864 is_ref: bool,
18865) CompileError!Air.Inst.Ref {
18866 const pt = sema.pt;
18867 const zcu = pt.zcu;
18868 const ip = &zcu.intern_pool;
18869
18870 var root_msg: ?*Zcu.ErrorMsg = null;
18871 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
18872
18873 switch (ip.indexToKey(struct_ty.toIntern())) {
18874 .tuple_type => |tuple| {
18875 // We can't get the slices, as the coercion may invalidate them.
18876 for (0..tuple.types.len) |i| {
18877 if (field_inits[i] != .none) {
18878 // Coerce the init value to the field type.
18879 const field_src = block.src(.{ .init_elem = .{
18880 .init_node_offset = init_src.offset.node_offset.x,
18881 .elem_index = @intCast(i),
18882 } });
18883 const field_ty: Type = .fromInterned(tuple.types.get(ip)[i]);
18884 field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src);
18885 continue;
18886 }
18887
18888 const default_val = tuple.values.get(ip)[i];
18889
18890 if (default_val == .none) {
18891 const template = "missing tuple field with index {d}";
18892 if (root_msg) |msg| {
18893 try sema.errNote(init_src, msg, template, .{i});
18894 } else {
18895 root_msg = try sema.errMsg(init_src, template, .{i});
18896 }
18897 } else {
18898 field_inits[i] = Air.internedToRef(default_val);
18899 }
18900 }
18901 },
18902 .struct_type => {
18903 const struct_type = ip.loadStructType(struct_ty.toIntern());
18904 for (0..struct_type.field_types.len) |i| {
18905 if (field_inits[i] != .none) {
18906 // Coerce the init value to the field type.
18907 const field_src = block.src(.{ .init_elem = .{
18908 .init_node_offset = init_src.offset.node_offset.x,
18909 .elem_index = @intCast(i),
18910 } });
18911 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
18912 field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src);
18913 continue;
18914 }
18915
18916 if (struct_type.field_is_comptime_bits.get(ip, i)) {
18917 field_inits[i] = .fromIntern(struct_type.field_defaults.get(ip)[i]);
18918 continue;
18919 }
18920
18921 try sema.ensureStructDefaultsResolved(struct_ty, init_src);
18922
18923 const field_default: InternPool.Index = d: {
18924 if (struct_type.field_defaults.len == 0) break :d .none;
18925 break :d struct_type.field_defaults.get(ip)[i];
18926 };
18927 if (field_default != .none) {
18928 field_inits[i] = .fromIntern(field_default);
18929 continue;
18930 }
18931
18932 const field_name = struct_type.field_names.get(ip)[i];
18933 const template = "missing struct field: {f}";
18934 const args = .{field_name.fmt(ip)};
18935 if (root_msg) |msg| {
18936 try sema.errNote(init_src, msg, template, args);
18937 } else {
18938 root_msg = try sema.errMsg(init_src, template, args);
18939 }
18940 }
18941 },
18942 else => unreachable,
18943 }
18944
18945 if (root_msg) |msg| {
18946 try sema.addDeclaredHereNote(msg, struct_ty);
18947 root_msg = null;
18948 return sema.failWithOwnedErrorMsg(block, msg);
18949 }
18950
18951 // Find which field forces the expression to be runtime, if any.
18952 const opt_runtime_index = for (field_inits, field_assign_idxs) |field_init, field_assign| {
18953 if (!(try sema.isComptimeKnown(field_init))) {
18954 break field_assign;
18955 }
18956 } else null;
18957
18958 const runtime_index = opt_runtime_index orelse switch (struct_ty.containerLayout(zcu)) {
18959 .auto, .@"extern" => {
18960 const elems = try sema.arena.alloc(InternPool.Index, field_inits.len);
18961 for (elems, field_inits) |*elem, field_init| {
18962 elem.* = sema.resolveValue(field_init).?.toIntern();
18963 }
18964 const struct_val = try pt.aggregateValue(struct_ty, elems);
18965 const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
18966 return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);
18967 },
18968 .@"packed" => {
18969 const buf = try sema.arena.alloc(u8, @intCast(@divCeil(struct_ty.bitSize(zcu), 8)));
18970 @memset(buf, 0);
18971 var bit_offset: u16 = 0;
18972 for (field_inits) |field_init| {
18973 const field_val = sema.resolveValue(field_init).?;
18974 field_val.writeToPackedMemory(zcu, buf, bit_offset);
18975 bit_offset += @intCast(field_val.typeOf(zcu).bitSize(zcu));
18976 }
18977 assert(bit_offset == struct_ty.bitSize(zcu));
18978 const struct_val: Value = try .readFromPackedMemory(struct_ty, pt, buf, 0);
18979 const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src);
18980 return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref);
18981 },
18982 };
18983
18984 if (struct_ty.comptimeOnly(zcu)) {
18985 return sema.failWithNeededComptime(block, block.src(.{ .init_elem = .{
18986 .init_node_offset = init_src.offset.node_offset.x,
18987 .elem_index = @intCast(runtime_index),
18988 } }), .{ .comptime_only = .{
18989 .ty = struct_ty,
18990 .msg = .struct_init,
18991 } });
18992 }
18993
18994 for (field_inits) |field_init| {
18995 try sema.validateRuntimeValue(block, dest_src, field_init);
18996 }
18997
18998 if (is_ref) {
18999 const target = zcu.getTarget();
19000 const alloc_ty = try pt.ptrType(.{
19001 .child = result_ty.toIntern(),
19002 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
19003 });
19004 const alloc = try block.addTy(.alloc, alloc_ty);
19005 const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src);
19006 for (field_inits, 0..) |field_init, i_usize| {
19007 const i: u32 = @intCast(i_usize);
19008 const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, struct_ty);
19009 try sema.storePtr(block, dest_src, field_ptr, field_init);
19010 }
19011
19012 return sema.makePtrConst(block, alloc);
19013 }
19014
19015 try sema.requireRuntimeBlock(block, dest_src, block.src(.{ .init_elem = .{
19016 .init_node_offset = init_src.offset.node_offset.x,
19017 .elem_index = @intCast(runtime_index),
19018 } }));
19019 const struct_val = try block.addAggregateInit(struct_ty, field_inits);
19020 return sema.coerce(block, result_ty, struct_val, init_src);
19021}
19022
19023fn zirStructInitAnon(
19024 sema: *Sema,
19025 block: *Block,
19026 inst: Zir.Inst.Index,
19027) CompileError!Air.Inst.Ref {
19028 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
19029 const src = block.nodeOffset(inst_data.src_node);
19030 const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
19031 return sema.structInitAnon(block, src, inst, .anon_init, extra.data, extra.end, false);
19032}
19033
19034fn structInitAnon(
19035 sema: *Sema,
19036 block: *Block,
19037 src: LazySrcLoc,
19038 inst: Zir.Inst.Index,
19039 /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a
19040 /// generic poison type. In this case, we need to know to interpret the extra data differently.
19041 comptime kind: enum { anon_init, typed_init },
19042 extra_data: switch (kind) {
19043 .anon_init => Zir.Inst.StructInitAnon,
19044 .typed_init => Zir.Inst.StructInit,
19045 },
19046 extra_end: usize,
19047 is_ref: bool,
19048) CompileError!Air.Inst.Ref {
19049 const pt = sema.pt;
19050 const zcu = pt.zcu;
19051 const comp = zcu.comp;
19052 const gpa = comp.gpa;
19053 const io = comp.io;
19054 const ip = &zcu.intern_pool;
19055
19056 const zir_datas = sema.code.instructions.items(.data);
19057
19058 const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len);
19059 const values = try sema.arena.alloc(InternPool.Index, types.len);
19060 const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len);
19061
19062 var any_values = false;
19063
19064 // Find which field forces the expression to be runtime, if any.
19065 const opt_runtime_index = rs: {
19066 var runtime_index: ?usize = null;
19067 var extra_index = extra_end;
19068 for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| {
19069 const item = switch (kind) {
19070 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
19071 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
19072 };
19073 extra_index = item.end;
19074
19075 const name = switch (kind) {
19076 .anon_init => sema.code.nullTerminatedString(item.data.field_name),
19077 .typed_init => name: {
19078 // `item.data.field_type` references a `field_type` instruction
19079 const field_type_data = zir_datas[@backingInt(item.data.field_type)].pl_node;
19080 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index);
19081 break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);
19082 },
19083 };
19084
19085 field_name.* = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
19086
19087 const init = sema.resolveInst(item.data.init);
19088 field_ty.* = sema.typeOf(init).toIntern();
19089 if (Type.fromInterned(field_ty.*).zigTypeTag(zcu) == .@"opaque") {
19090 const msg = msg: {
19091 const field_src = block.src(.{ .init_elem = .{
19092 .init_node_offset = src.offset.node_offset.x,
19093 .elem_index = @intCast(i_usize),
19094 } });
19095 const msg = try sema.errMsg(field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
19096 errdefer msg.destroy(sema.gpa);
19097
19098 try sema.addDeclaredHereNote(msg, .fromInterned(field_ty.*));
19099 break :msg msg;
19100 };
19101 return sema.failWithOwnedErrorMsg(block, msg);
19102 }
19103 if (sema.resolveValue(init)) |init_val| {
19104 field_val.* = init_val.toIntern();
19105 any_values = true;
19106 } else {
19107 field_val.* = .none;
19108 runtime_index = @intCast(i_usize);
19109 }
19110 }
19111 break :rs runtime_index;
19112 };
19113
19114 // A field can't be `comptime` if it references a `comptime var` but the aggregate can still be comptime-known.
19115 // Replace these fields with `.none` only for generating the type.
19116 const values_no_comptime = if (!any_values) values else blk: {
19117 const new_values = try sema.arena.alloc(InternPool.Index, types.len);
19118 for (values, new_values) |val, *new_val| {
19119 if (val != .none and Value.fromInterned(val).canMutateComptimeVarState(zcu)) {
19120 new_val.* = .none;
19121 } else new_val.* = val;
19122 }
19123 break :blk new_values;
19124 };
19125
19126 // We treat anonymous struct types as reified types, because there are similarities: they have
19127 // no captures, and instead use a form of structural equivalence which we can easy represent by
19128 // hashing the field names/types/values. They also perform layout resolution immediately. These
19129 // similarities mean that other code should actually treat anon struct types and reified struct
19130 // types identically anyway, so sharing the representation makes everything simpler.
19131 const type_hash: u64 = hash: {
19132 var hasher = std.hash.Wyhash.init(0);
19133 hasher.update(std.mem.sliceAsBytes(types));
19134 hasher.update(std.mem.sliceAsBytes(values_no_comptime));
19135 hasher.update(std.mem.sliceAsBytes(names));
19136 break :hash hasher.final();
19137 };
19138 const tracked_inst = try block.trackZir(inst);
19139 const struct_ty: Type = switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{
19140 .zir_index = tracked_inst,
19141 .type_hash = type_hash,
19142 .fields_len = extra_data.fields_len,
19143 .layout = .auto,
19144 .any_comptime_fields = any_values,
19145 .any_field_defaults = any_values,
19146 .any_field_aligns = false,
19147 .packed_backing_int_type = .none,
19148 })) {
19149 .existing => |ty| .fromInterned(ty),
19150 .wip => |wip| ty: {
19151 errdefer wip.cancel(ip, pt.tid);
19152 try sema.setTypeName(block, &wip, .anon, "struct", inst);
19153
19154 // Reified structs have field information populated immediately.
19155 @memcpy(wip.field_names.get(ip), names);
19156 @memcpy(wip.field_types.get(ip), types);
19157 if (any_values) {
19158 @memcpy(wip.field_values.get(ip), values_no_comptime);
19159 @memset(wip.field_is_comptime_bits.getAll(ip), 0);
19160 for (values_no_comptime, 0..) |val, field_index| {
19161 if (val == .none) continue;
19162 const bit_bag_index = field_index / 32;
19163 const mask = @as(u32, 1) << @intCast(field_index % 32);
19164 wip.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask;
19165 }
19166 }
19167
19168 const new_namespace_index = try pt.createNamespace(.{
19169 .parent = block.namespace.toOptional(),
19170 .owner_type = wip.index,
19171 .file_scope = block.getFileScopeIndex(zcu),
19172 .generation = zcu.generation,
19173 });
19174 errdefer pt.destroyNamespace(new_namespace_index);
19175 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
19176 break :ty .fromInterned(wip.finish(ip, new_namespace_index));
19177 },
19178 };
19179 try sema.addTypeReferenceEntry(src, struct_ty);
19180 // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty.
19181 try sema.ensureLayoutResolved(struct_ty, src, .init);
19182
19183 _ = opt_runtime_index orelse {
19184 const struct_val = try pt.aggregateValue(struct_ty, values);
19185 return sema.addConstantMaybeRef(struct_val, is_ref);
19186 };
19187
19188 for (values, 0..) |field_val, i| {
19189 if (field_val == .none) continue; // runtime-known
19190 const field_src = block.src(.{ .init_elem = .{
19191 .init_node_offset = src.offset.node_offset.x,
19192 .elem_index = @intCast(i),
19193 } });
19194 try sema.validateRuntimeValue(block, field_src, .fromIntern(field_val));
19195 }
19196
19197 if (is_ref) {
19198 const target = zcu.getTarget();
19199 const alloc_ty = try pt.ptrType(.{
19200 .child = struct_ty.toIntern(),
19201 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
19202 });
19203 const alloc = try block.addTy(.alloc, alloc_ty);
19204 var extra_index = extra_end;
19205 for (types, 0..) |field_ty, i_usize| {
19206 const i: u32 = @intCast(i_usize);
19207 const item = switch (kind) {
19208 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
19209 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
19210 };
19211 extra_index = item.end;
19212
19213 const field_ptr_ty = try pt.ptrType(.{
19214 .child = field_ty,
19215 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
19216 });
19217 if (values[i] == .none) {
19218 const init = sema.resolveInst(item.data.init);
19219 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
19220 _ = try block.addBinOp(.store, field_ptr, init);
19221 }
19222 }
19223
19224 return sema.makePtrConst(block, alloc);
19225 }
19226
19227 const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len);
19228 var extra_index = extra_end;
19229 for (types, 0..) |_, i| {
19230 const item = switch (kind) {
19231 .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index),
19232 .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index),
19233 };
19234 extra_index = item.end;
19235 element_refs[i] = sema.resolveInst(item.data.init);
19236 }
19237
19238 return block.addAggregateInit(struct_ty, element_refs);
19239}
19240
19241fn zirArrayInit(
19242 sema: *Sema,
19243 block: *Block,
19244 inst: Zir.Inst.Index,
19245 is_ref: bool,
19246) CompileError!Air.Inst.Ref {
19247 const pt = sema.pt;
19248 const zcu = pt.zcu;
19249 const gpa = sema.gpa;
19250 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
19251 const src = block.nodeOffset(inst_data.src_node);
19252
19253 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
19254 const args = sema.code.refSlice(extra.end, extra.data.operands_len);
19255 assert(args.len >= 2); // array_ty + at least one element
19256
19257 const result_ty = try sema.resolveTypeOrPoison(block, src, args[0]) orelse {
19258 // The type wasn't actually known, so treat this as an anon array init.
19259 return sema.arrayInitAnon(block, src, args[1..], is_ref);
19260 };
19261 const array_ty = result_ty.optEuBaseType(zcu);
19262 const is_tuple = array_ty.zigTypeTag(zcu) == .@"struct";
19263 const sentinel_val = array_ty.sentinel(zcu);
19264
19265 var root_msg: ?*Zcu.ErrorMsg = null;
19266 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
19267
19268 const final_len = try sema.usizeCast(block, src, array_ty.arrayLenIncludingSentinel(zcu));
19269 const resolved_args = try gpa.alloc(Air.Inst.Ref, final_len);
19270 defer gpa.free(resolved_args);
19271 for (resolved_args, 0..) |*dest, i| {
19272 const elem_src = block.src(.{ .init_elem = .{
19273 .init_node_offset = src.offset.node_offset.x,
19274 .elem_index = @intCast(i),
19275 } });
19276 // Less inits than needed.
19277 if (i + 2 > args.len) if (is_tuple) {
19278 const default_val = array_ty.structFieldDefaultValue(i, zcu) orelse {
19279 const template = "missing tuple field with index {d}";
19280 if (root_msg) |msg| {
19281 try sema.errNote(src, msg, template, .{i});
19282 } else {
19283 root_msg = try sema.errMsg(src, template, .{i});
19284 }
19285 continue;
19286 };
19287 dest.* = .fromValue(default_val);
19288 continue;
19289 } else {
19290 dest.* = Air.internedToRef(sentinel_val.?.toIntern());
19291 break;
19292 };
19293
19294 const arg = args[i + 1];
19295 const resolved_arg = sema.resolveInst(arg);
19296 const elem_ty = if (is_tuple)
19297 array_ty.fieldType(i, zcu)
19298 else
19299 array_ty.childType(zcu);
19300 dest.* = try sema.coerce(block, elem_ty, resolved_arg, elem_src);
19301 if (is_tuple) {
19302 if (try array_ty.structFieldValueComptime(pt, i)) |field_val| {
19303 const init_val = try sema.resolveConstValue(block, elem_src, dest.*, .{ .simple = .stored_to_comptime_field });
19304 if (!field_val.eql(init_val, elem_ty, zcu)) {
19305 return sema.failWithInvalidComptimeFieldStore(block, elem_src, array_ty, i);
19306 }
19307 }
19308 }
19309 }
19310
19311 if (root_msg) |msg| {
19312 try sema.addDeclaredHereNote(msg, array_ty);
19313 root_msg = null;
19314 return sema.failWithOwnedErrorMsg(block, msg);
19315 }
19316
19317 const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| {
19318 const comptime_known = try sema.isComptimeKnown(arg);
19319 if (!comptime_known) break @intCast(i);
19320 } else null;
19321
19322 _ = opt_runtime_index orelse {
19323 const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len);
19324 for (elem_vals, resolved_args) |*val, arg| {
19325 // We checked that all args are comptime above.
19326 val.* = sema.resolveValue(arg).?.toIntern();
19327 }
19328 const arr_val = try pt.aggregateValue(array_ty, elem_vals);
19329 const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val.toIntern()), src);
19330 const result_val = (sema.resolveValue(result_ref)).?;
19331 return sema.addConstantMaybeRef(result_val, is_ref);
19332 };
19333
19334 if (is_ref) {
19335 const target = zcu.getTarget();
19336 const alloc_ty = try pt.ptrType(.{
19337 .child = result_ty.toIntern(),
19338 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
19339 });
19340 const alloc = try block.addTy(.alloc, alloc_ty);
19341 const base_ptr = try sema.optEuBasePtrInit(block, alloc, src);
19342
19343 if (is_tuple) {
19344 for (resolved_args, 0..) |arg, i| {
19345 const elem_ptr_ty = try pt.ptrType(.{
19346 .child = array_ty.fieldType(i, zcu).toIntern(),
19347 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
19348 });
19349
19350 const index = try pt.intRef(.usize, i);
19351 const elem_ptr = try block.addPtrElemPtr(base_ptr, index, elem_ptr_ty);
19352 _ = try block.addBinOp(.store, elem_ptr, arg);
19353 }
19354 return sema.makePtrConst(block, alloc);
19355 }
19356
19357 const elem_ptr_ty = try pt.ptrType(.{
19358 .child = array_ty.childType(zcu).toIntern(),
19359 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
19360 });
19361
19362 for (resolved_args, 0..) |arg, i| {
19363 const index = try pt.intRef(.usize, i);
19364 const elem_ptr = try block.addPtrElemPtr(base_ptr, index, elem_ptr_ty);
19365 _ = try block.addBinOp(.store, elem_ptr, arg);
19366 }
19367 return sema.makePtrConst(block, alloc);
19368 }
19369
19370 const arr_ref = try block.addAggregateInit(array_ty, resolved_args);
19371 return sema.coerce(block, result_ty, arr_ref, src);
19372}
19373
19374fn zirArrayInitAnon(
19375 sema: *Sema,
19376 block: *Block,
19377 inst: Zir.Inst.Index,
19378) CompileError!Air.Inst.Ref {
19379 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
19380 const src = block.nodeOffset(inst_data.src_node);
19381 const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
19382 const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
19383 return sema.arrayInitAnon(block, src, operands, false);
19384}
19385
19386fn arrayInitAnon(
19387 sema: *Sema,
19388 block: *Block,
19389 src: LazySrcLoc,
19390 operands: []const Zir.Inst.Ref,
19391 is_ref: bool,
19392) CompileError!Air.Inst.Ref {
19393 const pt = sema.pt;
19394 const zcu = pt.zcu;
19395 const comp = zcu.comp;
19396 const gpa = comp.gpa;
19397 const io = comp.io;
19398 const ip = &zcu.intern_pool;
19399
19400 const types = try sema.arena.alloc(InternPool.Index, operands.len);
19401 const values = try sema.arena.alloc(InternPool.Index, operands.len);
19402
19403 var any_comptime = false;
19404 const opt_runtime_src = rs: {
19405 var runtime_src: ?LazySrcLoc = null;
19406 for (operands, 0..) |operand, i| {
19407 const operand_src = block.src(.{ .init_elem = .{
19408 .init_node_offset = src.offset.node_offset.x,
19409 .elem_index = @intCast(i),
19410 } });
19411 const elem = sema.resolveInst(operand);
19412 types[i] = sema.typeOf(elem).toIntern();
19413 if (Type.fromInterned(types[i]).zigTypeTag(zcu) == .@"opaque") {
19414 const msg = msg: {
19415 const msg = try sema.errMsg(operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
19416 errdefer msg.destroy(gpa);
19417
19418 try sema.addDeclaredHereNote(msg, .fromInterned(types[i]));
19419 break :msg msg;
19420 };
19421 return sema.failWithOwnedErrorMsg(block, msg);
19422 }
19423 if (sema.resolveValue(elem)) |val| {
19424 values[i] = val.toIntern();
19425 any_comptime = true;
19426 } else {
19427 values[i] = .none;
19428 runtime_src = operand_src;
19429 }
19430 }
19431 break :rs runtime_src;
19432 };
19433
19434 // A field can't be `comptime` if it references a `comptime var` but the aggregate can still be comptime-known.
19435 // Replace these fields with `.none` only for generating the type.
19436 const values_no_comptime = if (!any_comptime) values else blk: {
19437 const new_values = try sema.arena.alloc(InternPool.Index, operands.len);
19438 for (values, new_values) |val, *new_val| {
19439 if (val != .none and Value.fromInterned(val).canMutateComptimeVarState(zcu)) {
19440 new_val.* = .none;
19441 } else new_val.* = val;
19442 }
19443 break :blk new_values;
19444 };
19445
19446 const tuple_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{
19447 .types = types,
19448 .values = values_no_comptime,
19449 }));
19450
19451 _ = opt_runtime_src orelse {
19452 const tuple_val = try pt.aggregateValue(tuple_ty, values);
19453 return sema.addConstantMaybeRef(tuple_val, is_ref);
19454 };
19455
19456 for (operands, 0..) |operand, i| {
19457 const operand_src = block.src(.{ .init_elem = .{
19458 .init_node_offset = src.offset.node_offset.x,
19459 .elem_index = @intCast(i),
19460 } });
19461 try sema.validateRuntimeValue(block, operand_src, sema.resolveInst(operand));
19462 }
19463
19464 if (is_ref) {
19465 const target = sema.pt.zcu.getTarget();
19466 const alloc_ty = try pt.ptrType(.{
19467 .child = tuple_ty.toIntern(),
19468 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
19469 });
19470 const alloc = try block.addTy(.alloc, alloc_ty);
19471 for (operands, 0..) |operand, i_usize| {
19472 const i: u32 = @intCast(i_usize);
19473 const field_ptr_ty = try pt.ptrType(.{
19474 .child = types[i],
19475 .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) },
19476 });
19477 if (values[i] == .none) {
19478 const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty);
19479 _ = try block.addBinOp(.store, field_ptr, sema.resolveInst(operand));
19480 }
19481 }
19482
19483 return sema.makePtrConst(block, alloc);
19484 }
19485
19486 const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
19487 for (operands, 0..) |operand, i| {
19488 element_refs[i] = sema.resolveInst(operand);
19489 }
19490
19491 return block.addAggregateInit(tuple_ty, element_refs);
19492}
19493
19494fn addConstantMaybeRef(sema: *Sema, val: Value, is_ref: bool) !Air.Inst.Ref {
19495 return if (is_ref) sema.uavRef(val) else .fromValue(val);
19496}
19497
19498fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19499 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
19500 const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
19501 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
19502 const field_src = block.builtinCallArgSrc(inst_data.src_node, 1);
19503 const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type);
19504 const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name });
19505 try sema.ensureLayoutResolved(aggregate_ty, ty_src, .field_queried);
19506 return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src);
19507}
19508
19509fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19510 const pt = sema.pt;
19511 const zcu = pt.zcu;
19512 const comp = zcu.comp;
19513 const gpa = comp.gpa;
19514 const io = comp.io;
19515 const ip = &zcu.intern_pool;
19516
19517 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
19518 const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
19519 const ty_src = block.nodeOffset(inst_data.src_node);
19520 const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node });
19521 const wrapped_aggregate_ty = try sema.resolveTypeOrPoison(block, ty_src, extra.container_type) orelse return .generic_poison_type;
19522 const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu);
19523 const zir_field_name = sema.code.nullTerminatedString(extra.name_start);
19524 const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_field_name, .no_embedded_nulls);
19525 try sema.ensureLayoutResolved(aggregate_ty, ty_src, .init);
19526 return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src);
19527}
19528
19529/// Asserts that the layout of `aggregate_ty` is resolved.
19530fn fieldType(
19531 sema: *Sema,
19532 block: *Block,
19533 aggregate_ty: Type,
19534 field_name: InternPool.NullTerminatedString,
19535 field_src: LazySrcLoc,
19536 ty_src: LazySrcLoc,
19537) CompileError!Air.Inst.Ref {
19538 const pt = sema.pt;
19539 const zcu = pt.zcu;
19540 const ip = &zcu.intern_pool;
19541 aggregate_ty.assertHasLayout(zcu);
19542 var cur_ty = aggregate_ty;
19543 while (true) {
19544 switch (cur_ty.zigTypeTag(zcu)) {
19545 .@"struct" => switch (ip.indexToKey(cur_ty.toIntern())) {
19546 .tuple_type => |tuple| {
19547 const field_index = try sema.tupleFieldIndex(block, cur_ty, field_name, field_src);
19548 return Air.internedToRef(tuple.types.get(ip)[field_index]);
19549 },
19550 .struct_type => {
19551 const struct_type = ip.loadStructType(cur_ty.toIntern());
19552 const field_index = struct_type.nameIndex(ip, field_name) orelse
19553 return sema.failWithBadStructFieldAccess(block, cur_ty, struct_type, field_src, field_name);
19554 const field_ty = struct_type.field_types.get(ip)[field_index];
19555 return Air.internedToRef(field_ty);
19556 },
19557 else => unreachable,
19558 },
19559 .@"union" => {
19560 const union_obj = zcu.typeToUnion(cur_ty).?;
19561 const enum_obj = ip.loadEnumType(union_obj.enum_tag_type);
19562 const field_index = enum_obj.nameIndex(ip, field_name) orelse
19563 return sema.failWithBadUnionFieldAccess(block, cur_ty, union_obj, field_src, field_name);
19564 const field_ty = union_obj.field_types.get(ip)[field_index];
19565 return .fromIntern(field_ty);
19566 },
19567 .optional => {
19568 // Struct/array init through optional requires the child type to not be a pointer.
19569 // If the child of .optional is a pointer it'll error on the next loop.
19570 cur_ty = .fromInterned(ip.indexToKey(cur_ty.toIntern()).opt_type);
19571 continue;
19572 },
19573 .error_union => {
19574 cur_ty = cur_ty.errorUnionPayload(zcu);
19575 continue;
19576 },
19577 else => {},
19578 }
19579 return sema.fail(block, ty_src, "expected struct or union; found '{f}'", .{
19580 cur_ty.fmt(pt),
19581 });
19582 }
19583}
19584
19585fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
19586 return sema.getErrorReturnTrace(block);
19587}
19588
19589fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
19590 const pt = sema.pt;
19591 const zcu = pt.zcu;
19592 const ip = &zcu.intern_pool;
19593 const stack_trace_ty = try sema.getStdLangType(block.nodeOffset(.zero), .StackTrace);
19594 const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty);
19595 const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern());
19596
19597 switch (sema.owner.unwrap()) {
19598 .func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) {
19599 return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
19600 },
19601
19602 .@"comptime",
19603 .nav_ty,
19604 .nav_val,
19605 .type_layout,
19606 .struct_defaults,
19607 .memoized_state,
19608 => {},
19609 }
19610 return Air.internedToRef(try pt.intern(.{ .opt = .{
19611 .ty = opt_ptr_stack_trace_ty.toIntern(),
19612 .val = .none,
19613 } }));
19614}
19615
19616fn zirFrame(
19617 sema: *Sema,
19618 block: *Block,
19619 extended: Zir.Inst.Extended.InstData,
19620) CompileError!Air.Inst.Ref {
19621 const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand))));
19622 const src = block.nodeOffset(src_node);
19623 return sema.failWithUseOfAsync(block, src);
19624}
19625
19626fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19627 const pt = sema.pt;
19628 const zcu = pt.zcu;
19629 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
19630 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
19631 const ty = try sema.resolveType(block, operand_src, inst_data.operand);
19632 try sema.ensureLayoutResolved(ty, operand_src, .align_of);
19633 if (ty.isNoReturn(zcu)) {
19634 return sema.fail(block, operand_src, "no align available for uninstantiable type '{f}'", .{ty.fmt(sema.pt)});
19635 }
19636 return .fromValue(try pt.intValue(.comptime_int, ty.abiAlignment(zcu).toByteUnits().?));
19637}
19638
19639fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19640 const pt = sema.pt;
19641 const zcu = pt.zcu;
19642 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
19643 const src = block.nodeOffset(inst_data.src_node);
19644 const operand = sema.resolveInst(inst_data.operand);
19645 const operand_ty = sema.typeOf(operand);
19646 const is_vector = operand_ty.zigTypeTag(zcu) == .vector;
19647 const operand_scalar_ty = operand_ty.scalarType(zcu);
19648 if (operand_scalar_ty.toIntern() != .bool_type) {
19649 return sema.fail(block, src, "expected 'bool', found '{t}'", .{operand_scalar_ty.zigTypeTag(zcu)});
19650 }
19651 const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined;
19652 const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1;
19653 if (sema.resolveValue(operand)) |val| {
19654 if (!is_vector) {
19655 return if (val.isUndef(zcu)) .undef_u1 else if (val.toBool()) .one_u1 else .zero_u1;
19656 }
19657 if (val.isUndef(zcu)) return pt.undefRef(dest_ty);
19658 const new_elems = try sema.arena.alloc(InternPool.Index, len);
19659 for (new_elems, 0..) |*new_elem, i| {
19660 const old_elem = try val.elemValue(pt, i);
19661 new_elem.* = if (old_elem.isUndef(zcu))
19662 .undef_u1
19663 else if (old_elem.toBool())
19664 .one_u1
19665 else
19666 .zero_u1;
19667 }
19668 return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern());
19669 }
19670 return block.addTyOp(.bit_cast, dest_ty, operand);
19671}
19672
19673fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19674 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
19675 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
19676 const uncoerced_operand = sema.resolveInst(inst_data.operand);
19677 const operand = try sema.coerce(block, .anyerror, uncoerced_operand, operand_src);
19678
19679 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
19680 const err_name = sema.pt.zcu.intern_pool.indexToKey(val.toIntern()).err.name;
19681 return sema.addNullTerminatedStrLit(err_name);
19682 }
19683
19684 // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass
19685 // might be able to resolve the result at compile time.
19686 return block.addUnOp(.error_name, operand);
19687}
19688
19689fn zirAbs(
19690 sema: *Sema,
19691 block: *Block,
19692 inst: Zir.Inst.Index,
19693) CompileError!Air.Inst.Ref {
19694 const pt = sema.pt;
19695 const zcu = pt.zcu;
19696 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
19697 const operand = sema.resolveInst(inst_data.operand);
19698 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
19699 const operand_ty = sema.typeOf(operand);
19700 const scalar_ty = operand_ty.scalarType(zcu);
19701
19702 const result_ty = switch (scalar_ty.zigTypeTag(zcu)) {
19703 .comptime_float, .float, .comptime_int => operand_ty,
19704 .int => if (scalar_ty.isSignedInt(zcu)) try operand_ty.toUnsigned(pt) else return operand,
19705 else => return sema.fail(
19706 block,
19707 operand_src,
19708 "expected integer, float, or vector of either integers or floats, found '{f}'",
19709 .{operand_ty.fmt(pt)},
19710 ),
19711 };
19712
19713 return (try sema.maybeConstantUnaryMath(operand, result_ty, Value.abs)) orelse {
19714 try sema.requireRuntimeBlock(block, operand_src, null);
19715 return block.addTyOp(.abs, result_ty, operand);
19716 };
19717}
19718
19719fn maybeConstantUnaryMath(
19720 sema: *Sema,
19721 operand: Air.Inst.Ref,
19722 result_ty: Type,
19723 comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value,
19724) CompileError!?Air.Inst.Ref {
19725 const pt = sema.pt;
19726 const zcu = pt.zcu;
19727 switch (result_ty.zigTypeTag(zcu)) {
19728 .vector => if (sema.resolveValue(operand)) |val| {
19729 const scalar_ty = result_ty.scalarType(zcu);
19730 const vec_len = result_ty.vectorLen(zcu);
19731 if (val.isUndef(zcu))
19732 return try pt.undefRef(result_ty);
19733
19734 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
19735 for (elems, 0..) |*elem, i| {
19736 const elem_val = try val.elemValue(pt, i);
19737 elem.* = (try eval(elem_val, scalar_ty, sema.arena, pt)).toIntern();
19738 }
19739 return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern());
19740 },
19741 else => if (sema.resolveValue(operand)) |operand_val| {
19742 if (operand_val.isUndef(zcu))
19743 return try pt.undefRef(result_ty);
19744 const result_val = try eval(operand_val, result_ty, sema.arena, pt);
19745 return Air.internedToRef(result_val.toIntern());
19746 },
19747 }
19748 return null;
19749}
19750
19751fn unaryMath(
19752 sema: *Sema,
19753 block: *Block,
19754 operand_src: LazySrcLoc,
19755 operand: Air.Inst.Ref,
19756 air_tag: Air.Inst.Tag,
19757 comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value,
19758) CompileError!Air.Inst.Ref {
19759 const pt = sema.pt;
19760 const zcu = pt.zcu;
19761 const operand_ty = sema.typeOf(operand);
19762 const scalar_ty = operand_ty.scalarType(zcu);
19763
19764 switch (scalar_ty.zigTypeTag(zcu)) {
19765 .comptime_float, .float => {},
19766 else => return sema.fail(
19767 block,
19768 operand_src,
19769 "expected vector of floats or float type, found '{f}'",
19770 .{operand_ty.fmt(pt)},
19771 ),
19772 }
19773
19774 return (try sema.maybeConstantUnaryMath(operand, operand_ty, eval)) orelse {
19775 try sema.requireRuntimeBlock(block, operand_src, null);
19776 return block.addUnOp(air_tag, operand);
19777 };
19778}
19779
19780fn zirUnaryMath(
19781 sema: *Sema,
19782 block: *Block,
19783 inst: Zir.Inst.Index,
19784 air_tag: Air.Inst.Tag,
19785 comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value,
19786) CompileError!Air.Inst.Ref {
19787 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
19788 const operand = sema.resolveInst(inst_data.operand);
19789 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
19790
19791 return sema.unaryMath(block, operand_src, operand, air_tag, eval);
19792}
19793
19794fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19795 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
19796 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
19797 const src = block.nodeOffset(inst_data.src_node);
19798 const operand = sema.resolveInst(inst_data.operand);
19799 const operand_ty = sema.typeOf(operand);
19800 const pt = sema.pt;
19801 const zcu = pt.zcu;
19802 const ip = &zcu.intern_pool;
19803 const enum_ty = switch (operand_ty.zigTypeTag(zcu)) {
19804 .enum_literal => {
19805 const val = (try sema.resolveDefinedValue(block, operand_src, operand)).?;
19806 const tag_name = ip.indexToKey(val.toIntern()).enum_literal;
19807 return sema.addNullTerminatedStrLit(tag_name);
19808 },
19809 .@"enum" => operand_ty,
19810 .@"union" => operand_ty.unionTagType(zcu) orelse
19811 return sema.fail(block, src, "union '{f}' is untagged", .{operand_ty.fmt(pt)}),
19812 else => return sema.fail(block, operand_src, "expected enum or union; found '{f}'", .{
19813 operand_ty.fmt(pt),
19814 }),
19815 };
19816 const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
19817 if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
19818 const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse {
19819 const msg = msg: {
19820 const msg = try sema.errMsg(src, "no field with value '{f}' in enum '{f}'", .{
19821 val.fmtValueSema(pt, sema), enum_ty.fmt(pt),
19822 });
19823 errdefer msg.destroy(sema.gpa);
19824 try sema.errNote(enum_ty.srcLoc(zcu), msg, "declared here", .{});
19825 break :msg msg;
19826 };
19827 return sema.failWithOwnedErrorMsg(block, msg);
19828 };
19829 // TODO: write something like getCoercedInts to avoid needing to dupe
19830 const field_name = enum_ty.enumFieldName(field_index, zcu);
19831 return sema.addNullTerminatedStrLit(field_name);
19832 }
19833 try sema.requireRuntimeBlock(block, src, operand_src);
19834 if (block.wantSafety() and zcu.backendSupportsFeature(.is_named_enum_value)) {
19835 const ok = try block.addUnOp(.is_named_enum_value, casted_operand);
19836 try sema.addSafetyCheck(block, src, ok, .invalid_enum_value);
19837 }
19838 // In case the value is runtime-known, we have an AIR instruction for this instead
19839 // of trying to lower it in Sema because an optimization pass may result in the operand
19840 // being comptime-known, which would let us elide the `tag_name` AIR instruction.
19841 return block.addUnOp(.tag_name, casted_operand);
19842}
19843
19844fn zirReifyInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19845 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
19846 const signedness_src = block.builtinCallArgSrc(inst_data.src_node, 0);
19847 const bits_src = block.builtinCallArgSrc(inst_data.src_node, 1);
19848 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
19849 const signedness = try sema.resolveStdLangEnum(block, signedness_src, extra.lhs, .Signedness, .{ .simple = .int_signedness });
19850 const bits: u16 = @intCast(try sema.resolveInt(block, bits_src, extra.rhs, .u16, .{ .simple = .int_bit_width }));
19851 if (bits == 0 and signedness == .signed) {
19852 return sema.fail(block, bits_src, "signed integer cannot have bit width 0", .{});
19853 }
19854 return .fromType(try sema.pt.intType(signedness, bits));
19855}
19856
19857fn zirReifySliceArgTy(
19858 sema: *Sema,
19859 block: *Block,
19860 extended: Zir.Inst.Extended.InstData,
19861) CompileError!Air.Inst.Ref {
19862 const pt = sema.pt;
19863 const zcu = pt.zcu;
19864
19865 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
19866 const info: Zir.Inst.ReifySliceArgInfo = @fromBackingInt(@intCast(extended.small));
19867
19868 const src = block.nodeOffset(extra.node);
19869
19870 const comptime_reason: std.zig.SimpleComptimeReason, const in_scalar_ty: Type, const out_scalar_ty: Type = switch (info) {
19871 // zig fmt: off
19872 .type_to_fn_param_attrs => .{ .fn_param_attrs, .type, try sema.getStdLangType(src, .@"Type.Fn.ParamAttributes") },
19873 .string_to_struct_field_type => .{ .struct_field_types, .slice_const_u8, .type },
19874 .string_to_union_field_type => .{ .union_field_types, .slice_const_u8, .type },
19875 .string_to_struct_field_attrs => .{ .struct_field_attrs, .slice_const_u8, try sema.getStdLangType(src, .@"Type.Struct.FieldAttributes") },
19876 .string_to_union_field_attrs => .{ .union_field_attrs, .slice_const_u8, try sema.getStdLangType(src, .@"Type.Union.FieldAttributes") },
19877 // zig fmt: on
19878 };
19879
19880 const operand_ty = try pt.ptrType(.{
19881 .child = in_scalar_ty.toIntern(),
19882 .flags = .{ .size = .slice, .is_const = true },
19883 });
19884
19885 const operand_uncoerced = sema.resolveInst(extra.operand);
19886 const operand_coerced = try sema.coerce(block, operand_ty, operand_uncoerced, src);
19887 const operand_val = try sema.resolveConstDefinedValue(block, src, operand_coerced, .{ .simple = comptime_reason });
19888 const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len);
19889 if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null);
19890 const len = len_val.toUnsignedInt(zcu);
19891
19892 return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{
19893 .len = len,
19894 .child = out_scalar_ty.toIntern(),
19895 })));
19896}
19897
19898fn zirReifyEnumValueSliceTy(
19899 sema: *Sema,
19900 block: *Block,
19901 extended: Zir.Inst.Extended.InstData,
19902) CompileError!Air.Inst.Ref {
19903 const pt = sema.pt;
19904 const zcu = pt.zcu;
19905
19906 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
19907
19908 const int_tag_ty_src = block.builtinCallArgSrc(extra.node, 0);
19909 const field_names_src = block.builtinCallArgSrc(extra.node, 2);
19910
19911 const int_tag_ty = try sema.resolveType(block, int_tag_ty_src, extra.lhs);
19912
19913 const operand_uncoerced = sema.resolveInst(extra.rhs);
19914 const operand_coerced = try sema.coerce(block, .slice_const_slice_const_u8, operand_uncoerced, field_names_src);
19915 const operand_val = try sema.resolveConstDefinedValue(block, field_names_src, operand_coerced, .{ .simple = .enum_field_names });
19916 const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len);
19917 if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, field_names_src, null);
19918 const len = len_val.toUnsignedInt(zcu);
19919
19920 return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{
19921 .len = len,
19922 .child = int_tag_ty.toIntern(),
19923 })));
19924}
19925
19926fn zirReifyPointerSentinelTy(
19927 sema: *Sema,
19928 block: *Block,
19929 extended: Zir.Inst.Extended.InstData,
19930) CompileError!Air.Inst.Ref {
19931 const pt = sema.pt;
19932 const zcu = pt.zcu;
19933 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
19934 const src = block.nodeOffset(extra.node);
19935 const elem_ty = try sema.resolveType(block, src, extra.operand);
19936 return .fromType(switch (elem_ty.zigTypeTag(zcu)) {
19937 else => try pt.optionalType(elem_ty.toIntern()),
19938 // These types cannot be the child of an optional. To allow reifying pointers to them still,
19939 // we treat the "sentinel" argument to `@Pointer` as `?noreturn` instead of `?T`.
19940 .@"opaque", .null => .optional_noreturn,
19941 });
19942}
19943
19944fn zirReifyTuple(
19945 sema: *Sema,
19946 block: *Block,
19947 extended: Zir.Inst.Extended.InstData,
19948) CompileError!Air.Inst.Ref {
19949 const pt = sema.pt;
19950 const zcu = pt.zcu;
19951 const comp = zcu.comp;
19952 const gpa = comp.gpa;
19953 const io = comp.io;
19954
19955 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
19956 const operand_src = block.builtinCallArgSrc(extra.node, 0);
19957
19958 const types_uncoerced = sema.resolveInst(extra.operand);
19959 const types_coerced = try sema.coerce(block, .slice_const_type, types_uncoerced, operand_src);
19960 const types_slice_val = try sema.resolveConstDefinedValue(block, operand_src, types_coerced, .{ .simple = .tuple_field_types });
19961 const types_array_val = try sema.derefSliceAsArray(block, operand_src, types_slice_val, .{ .simple = .tuple_field_types });
19962 const fields_len: u32 = @intCast(types_array_val.typeOf(zcu).arrayLen(zcu));
19963
19964 const field_types = try sema.arena.alloc(InternPool.Index, fields_len);
19965 for (field_types, 0..) |*field_ty, field_idx| {
19966 const field_ty_val = try types_array_val.elemValue(pt, field_idx);
19967 if (field_ty_val.isUndef(zcu)) {
19968 return sema.failWithUseOfUndef(block, operand_src, null);
19969 }
19970 try sema.validateTupleFieldType(block, field_ty_val.toType(), operand_src);
19971 field_ty.* = field_ty_val.toIntern();
19972 }
19973
19974 const field_values = try sema.arena.alloc(InternPool.Index, fields_len);
19975 @memset(field_values, .none);
19976
19977 return .fromIntern(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{
19978 .types = field_types,
19979 .values = field_values,
19980 }));
19981}
19982
19983fn zirReifyPointer(
19984 sema: *Sema,
19985 block: *Block,
19986 extended: Zir.Inst.Extended.InstData,
19987) CompileError!Air.Inst.Ref {
19988 const pt = sema.pt;
19989 const zcu = pt.zcu;
19990 const comp = zcu.comp;
19991 const gpa = comp.gpa;
19992 const io = comp.io;
19993 const ip = &zcu.intern_pool;
19994
19995 const extra = sema.code.extraData(Zir.Inst.ReifyPointer, extended.operand).data;
19996 const src = block.nodeOffset(extra.node);
19997 const size_src = block.builtinCallArgSrc(extra.node, 0);
19998 const attrs_src = block.builtinCallArgSrc(extra.node, 1);
19999 const elem_ty_src = block.builtinCallArgSrc(extra.node, 2);
20000 const sentinel_src = block.builtinCallArgSrc(extra.node, 3);
20001
20002 const size_ty = try sema.getStdLangType(size_src, .@"Type.Pointer.Size");
20003 const attrs_ty = try sema.getStdLangType(attrs_src, .@"Type.Pointer.Attributes");
20004
20005 const size_uncoerced = sema.resolveInst(extra.size);
20006 const size_coerced = try sema.coerce(block, size_ty, size_uncoerced, size_src);
20007 const size_val = try sema.resolveConstDefinedValue(block, size_src, size_coerced, .{ .simple = .pointer_size });
20008 const size = try sema.interpretStdLangType(block, size_src, size_val, std.lang.Type.Pointer.Size);
20009
20010 const attrs_uncoerced = sema.resolveInst(extra.attrs);
20011 const attrs_coerced = try sema.coerce(block, attrs_ty, attrs_uncoerced, attrs_src);
20012 const attrs_val = try sema.resolveConstDefinedValue(block, attrs_src, attrs_coerced, .{ .simple = .pointer_attrs });
20013 const attrs = try sema.interpretStdLangType(block, attrs_src, attrs_val, std.lang.Type.Pointer.Attributes);
20014
20015 const @"align": Alignment = if (attrs.@"align") |bytes| a: {
20016 break :a try sema.validateAlign(block, attrs_src, bytes);
20017 } else .none;
20018
20019 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_ty);
20020
20021 switch (elem_ty.zigTypeTag(zcu)) {
20022 .noreturn => return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}),
20023 // This needs to be disallowed, because the sentinel parameter would otherwise have type
20024 // `?@TypeOf(null)`, which is not a valid type because you cannot differentiate between
20025 // constructing the "inner" null value and the "outer" null value.
20026 .null => return sema.fail(block, elem_ty_src, "cannot reify pointer to '@TypeOf(null)'", .{}),
20027 .@"fn" => switch (size) {
20028 .one => {},
20029 .many, .c, .slice => return sema.fail(block, src, "function pointers must be single pointers", .{}),
20030 },
20031 .@"opaque" => switch (size) {
20032 .one => {},
20033 .many, .c, .slice => return sema.fail(block, src, "indexable pointer to opaque type '{f}' not allowed", .{elem_ty.fmt(pt)}),
20034 },
20035 else => {},
20036 }
20037
20038 const sentinel_ty = try pt.optionalType(elem_ty.toIntern());
20039 const sentinel_uncoerced = sema.resolveInst(extra.sentinel);
20040 const sentinel_coerced = try sema.coerce(block, sentinel_ty, sentinel_uncoerced, sentinel_src);
20041 const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel_coerced, .{ .simple = .pointer_sentinel });
20042 const opt_sentinel = sentinel_val.optionalValue(zcu);
20043 if (opt_sentinel) |sentinel| {
20044 switch (size) {
20045 .many, .slice => {},
20046 .one, .c => return sema.fail(block, sentinel_src, "sentinels are only allowed on slices and unknown-length pointers", .{}),
20047 }
20048 try checkSentinelType(sema, block, sentinel_src, elem_ty);
20049 if (sentinel.canMutateComptimeVarState(zcu)) {
20050 const sentinel_name = try ip.getOrPutString(gpa, io, pt.tid, "sentinel", .no_embedded_nulls);
20051 return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", sentinel);
20052 }
20053 }
20054
20055 return .fromType(try pt.ptrType(.{
20056 .child = elem_ty.toIntern(),
20057 .sentinel = if (opt_sentinel) |s| s.toIntern() else .none,
20058 .flags = .{
20059 .size = size,
20060 .is_const = attrs.@"const",
20061 .is_volatile = attrs.@"volatile",
20062 .is_allowzero = attrs.@"allowzero",
20063 .address_space = attrs.@"addrspace" orelse as: {
20064 if (elem_ty.zigTypeTag(zcu) == .@"fn" and zcu.getTarget().cpu.arch == .avr) break :as .flash;
20065 break :as .generic;
20066 },
20067 .alignment = @"align",
20068 },
20069 }));
20070}
20071
20072fn zirReifyFn(
20073 sema: *Sema,
20074 block: *Block,
20075 extended: Zir.Inst.Extended.InstData,
20076) CompileError!Air.Inst.Ref {
20077 const pt = sema.pt;
20078 const zcu = pt.zcu;
20079 const comp = zcu.comp;
20080 const gpa = comp.gpa;
20081 const io = comp.io;
20082 const ip = &zcu.intern_pool;
20083
20084 const extra = sema.code.extraData(Zir.Inst.ReifyFn, extended.operand).data;
20085 const param_types_src = block.builtinCallArgSrc(extra.node, 0);
20086 const param_attrs_src = block.builtinCallArgSrc(extra.node, 1);
20087 const ret_ty_src = block.builtinCallArgSrc(extra.node, 2);
20088 const fn_attrs_src = block.builtinCallArgSrc(extra.node, 3);
20089
20090 const single_param_attrs_ty = try sema.getStdLangType(param_attrs_src, .@"Type.Fn.ParamAttributes");
20091 const fn_attrs_ty = try sema.getStdLangType(fn_attrs_src, .@"Type.Fn.Attributes");
20092
20093 const param_types_uncoerced = sema.resolveInst(extra.param_types);
20094 const param_types_coerced = try sema.coerce(block, .slice_const_type, param_types_uncoerced, param_types_src);
20095 const param_types_slice = try sema.resolveConstDefinedValue(block, param_types_src, param_types_coerced, .{ .simple = .fn_param_types });
20096 const param_types_arr = try sema.derefSliceAsArray(block, param_types_src, param_types_slice, .{ .simple = .fn_param_types });
20097
20098 const params_len = param_types_arr.typeOf(zcu).arrayLen(zcu);
20099
20100 const param_attrs_ty = try pt.singleConstPtrType(try pt.arrayType(.{
20101 .len = params_len,
20102 .child = single_param_attrs_ty.toIntern(),
20103 }));
20104 const param_attrs_uncoerced = sema.resolveInst(extra.param_attrs);
20105 const param_attrs_coerced = try sema.coerce(block, param_attrs_ty, param_attrs_uncoerced, param_attrs_src);
20106 const param_attrs_slice = try sema.resolveConstDefinedValue(block, param_attrs_src, param_attrs_coerced, .{ .simple = .fn_param_attrs });
20107 const param_attrs_arr = try sema.derefSliceAsArray(block, param_attrs_src, param_attrs_slice, .{ .simple = .fn_param_attrs });
20108
20109 const ret_ty = try sema.resolveType(block, ret_ty_src, extra.ret_ty);
20110
20111 const fn_attrs_uncoerced = sema.resolveInst(extra.fn_attrs);
20112 const fn_attrs_coerced = try sema.coerce(block, fn_attrs_ty, fn_attrs_uncoerced, fn_attrs_src);
20113 const fn_attrs_val = try sema.resolveConstDefinedValue(block, fn_attrs_src, fn_attrs_coerced, .{ .simple = .fn_attrs });
20114 const fn_attrs = try sema.interpretStdLangType(block, fn_attrs_src, fn_attrs_val, std.lang.Type.Fn.Attributes);
20115
20116 var noalias_bits: u32 = 0;
20117 const param_types_ip = try sema.arena.alloc(InternPool.Index, @intCast(params_len));
20118 for (param_types_ip, 0..@intCast(params_len)) |*param_ty_ip, param_idx| {
20119 const param_ty: Type = (try param_types_arr.elemValue(pt, param_idx)).toType();
20120 const param_attrs = try sema.interpretStdLangType(
20121 block,
20122 param_attrs_src,
20123 try param_attrs_arr.elemValue(pt, param_idx),
20124 std.lang.Type.Fn.ParamAttributes,
20125 );
20126 try sema.checkParamType(
20127 block,
20128 @intCast(param_idx),
20129 param_ty,
20130 false,
20131 param_attrs.@"noalias",
20132 param_types_src,
20133 fn_attrs.@"callconv",
20134 );
20135 if (param_attrs.@"noalias") {
20136 if (param_idx > 31) {
20137 return sema.fail(block, param_attrs_src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{});
20138 }
20139 noalias_bits |= @as(u32, 1) << @intCast(param_idx);
20140 }
20141 param_ty_ip.* = param_ty.toIntern();
20142 }
20143
20144 if (fn_attrs.varargs) {
20145 try sema.checkCallConvSupportsVarArgs(block, fn_attrs_src, fn_attrs.@"callconv");
20146 }
20147
20148 try sema.checkReturnTypeAndCallConv(
20149 block,
20150 ret_ty,
20151 ret_ty_src,
20152 fn_attrs.@"callconv",
20153 fn_attrs_src,
20154 if (fn_attrs.varargs) fn_attrs_src else null,
20155 false,
20156 false,
20157 );
20158
20159 return .fromIntern(try ip.getFuncType(gpa, io, pt.tid, .{
20160 .param_types = param_types_ip,
20161 .noalias_bits = noalias_bits,
20162 .comptime_bits = 0,
20163 .return_type = ret_ty.toIntern(),
20164 .cc = fn_attrs.@"callconv",
20165 .is_var_args = fn_attrs.varargs,
20166 .is_noinline = false,
20167 }));
20168}
20169
20170fn zirReifyStruct(
20171 sema: *Sema,
20172 block: *Block,
20173 extended: Zir.Inst.Extended.InstData,
20174 inst: Zir.Inst.Index,
20175) CompileError!Air.Inst.Ref {
20176 const pt = sema.pt;
20177 const zcu = pt.zcu;
20178 const comp = zcu.comp;
20179 const gpa = comp.gpa;
20180 const io = comp.io;
20181 const ip = &zcu.intern_pool;
20182
20183 const name_strategy: Zir.Inst.NameStrategy = @fromBackingInt(@intCast(extended.small));
20184 const extra = sema.code.extraData(Zir.Inst.ReifyStruct, extended.operand).data;
20185 const tracked_inst = try block.trackZir(inst);
20186
20187 const src: LazySrcLoc = .{
20188 .base_node_inst = tracked_inst,
20189 .offset = .nodeOffset(.zero),
20190 };
20191
20192 const layout_src: LazySrcLoc = .{
20193 .base_node_inst = tracked_inst,
20194 .offset = .{ .node_offset_builtin_call_arg = .{
20195 .builtin_call_node = .zero,
20196 .arg_index = 0,
20197 } },
20198 };
20199 const backing_ty_src: LazySrcLoc = .{
20200 .base_node_inst = tracked_inst,
20201 .offset = .{ .node_offset_builtin_call_arg = .{
20202 .builtin_call_node = .zero,
20203 .arg_index = 1,
20204 } },
20205 };
20206 const field_names_src: LazySrcLoc = .{
20207 .base_node_inst = tracked_inst,
20208 .offset = .{ .node_offset_builtin_call_arg = .{
20209 .builtin_call_node = .zero,
20210 .arg_index = 2,
20211 } },
20212 };
20213 const field_types_src: LazySrcLoc = .{
20214 .base_node_inst = tracked_inst,
20215 .offset = .{ .node_offset_builtin_call_arg = .{
20216 .builtin_call_node = .zero,
20217 .arg_index = 3,
20218 } },
20219 };
20220 const field_attrs_src: LazySrcLoc = .{
20221 .base_node_inst = tracked_inst,
20222 .offset = .{ .node_offset_builtin_call_arg = .{
20223 .builtin_call_node = .zero,
20224 .arg_index = 4,
20225 } },
20226 };
20227
20228 const container_layout_ty = try sema.getStdLangType(layout_src, .@"Type.ContainerLayout");
20229 const single_field_attrs_ty = try sema.getStdLangType(field_attrs_src, .@"Type.Struct.FieldAttributes");
20230
20231 const layout_uncoerced = sema.resolveInst(extra.layout);
20232 const layout_coerced = try sema.coerce(block, container_layout_ty, layout_uncoerced, layout_src);
20233 const layout_val = try sema.resolveConstDefinedValue(block, layout_src, layout_coerced, .{ .simple = .struct_layout });
20234 const layout = try sema.interpretStdLangType(block, layout_src, layout_val, std.lang.Type.ContainerLayout);
20235
20236 const backing_int_ty_uncoerced = sema.resolveInst(extra.backing_ty);
20237 const backing_int_ty_coerced = try sema.coerce(block, .optional_type, backing_int_ty_uncoerced, backing_ty_src);
20238 const backing_int_ty_val = try sema.resolveConstDefinedValue(block, backing_ty_src, backing_int_ty_coerced, .{ .simple = .packed_struct_backing_int_type });
20239
20240 const field_names_uncoerced = sema.resolveInst(extra.field_names);
20241 const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src);
20242 const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .struct_field_names });
20243 const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .struct_field_names });
20244
20245 const fields_len = try sema.usizeCast(block, src, field_names_arr.typeOf(zcu).arrayLen(zcu));
20246
20247 const field_types_ty = try pt.singleConstPtrType(try pt.arrayType(.{
20248 .len = fields_len,
20249 .child = .type_type,
20250 }));
20251 const field_attrs_ty = try pt.singleConstPtrType(try pt.arrayType(.{
20252 .len = fields_len,
20253 .child = single_field_attrs_ty.toIntern(),
20254 }));
20255
20256 const field_types_uncoerced = sema.resolveInst(extra.field_types);
20257 const field_types_coerced = try sema.coerce(block, field_types_ty, field_types_uncoerced, field_types_src);
20258 const field_types_slice = try sema.resolveConstDefinedValue(block, field_types_src, field_types_coerced, .{ .simple = .struct_field_types });
20259 const field_types_arr = try sema.derefSliceAsArray(block, field_types_src, field_types_slice, .{ .simple = .struct_field_types });
20260
20261 const field_attrs_uncoerced = sema.resolveInst(extra.field_attrs);
20262 const field_attrs_coerced = try sema.coerce(block, field_attrs_ty, field_attrs_uncoerced, field_attrs_src);
20263 const field_attrs_slice = try sema.resolveConstDefinedValue(block, field_attrs_src, field_attrs_coerced, .{ .simple = .struct_field_attrs });
20264 const field_attrs_arr = try sema.derefSliceAsArray(block, field_attrs_src, field_attrs_slice, .{ .simple = .struct_field_attrs });
20265
20266 // Before we begin, check for undefs...
20267 if (try sema.anyUndef(block, field_attrs_src, field_attrs_arr)) {
20268 return sema.failWithUseOfUndef(block, field_attrs_src, null);
20269 }
20270 if (try sema.anyUndef(block, field_types_src, field_types_arr)) {
20271 return sema.failWithUseOfUndef(block, field_types_src, null);
20272 }
20273 // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us.
20274 if (try sema.anyUndef(block, backing_ty_src, backing_int_ty_val)) {
20275 return sema.failWithUseOfUndef(block, backing_ty_src, null);
20276 }
20277
20278 // Most validation of this type happens during type resolution. We basically need to do the work
20279 // which AstGen would normally do. An exception is checking for duplicate field names, which is
20280 // handled by type resolution---it just simplifies some logic a little.
20281
20282 // As well as validation, we're going to gather some information about the fields, and construct
20283 // a hash representing the inputs for deduplication purposes.
20284
20285 var any_comptime_fields = false;
20286 var any_field_defaults = false;
20287 var any_field_aligns = false;
20288
20289 // TODO: use a longer hash!
20290 var hasher = std.hash.Wyhash.init(0);
20291 std.hash.autoHash(&hasher, layout);
20292 std.hash.autoHash(&hasher, backing_int_ty_val);
20293
20294 const backing_int_ty: ?Type = if (backing_int_ty_val.optionalValue(zcu)) |backing| ty: {
20295 switch (layout) {
20296 .auto, .@"extern" => return sema.fail(block, backing_ty_src, "non-packed struct does not support backing integer type", .{}),
20297 .@"packed" => {},
20298 }
20299 break :ty backing.toType();
20300 } else null;
20301
20302 // The field *type* array has already been deduplicated for us thanks to the InternPool!
20303 std.hash.autoHash(&hasher, field_types_arr);
20304 // However, for field names and attributes, we need to actually iterate the individual fields,
20305 // because the presence of pointers (the `[]const u8` for the name and the `*const anyopaque`
20306 // for the default value) means that distinct interned values could ultimately result in the
20307 // same struct type.
20308 for (0..fields_len) |field_idx| {
20309 const field_name_val = try field_names_arr.elemValue(pt, field_idx);
20310 const field_attrs_val = try field_attrs_arr.elemValue(pt, field_idx);
20311
20312 const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, .{ .simple = .struct_field_names });
20313
20314 const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
20315 std.lang.Type.Struct.FieldAttributes,
20316 "comptime",
20317 ).?);
20318 const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
20319 std.lang.Type.Struct.FieldAttributes,
20320 "align",
20321 ).?);
20322 const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
20323 std.lang.Type.Struct.FieldAttributes,
20324 "default_value_ptr",
20325 ).?);
20326
20327 const field_default: InternPool.Index = d: {
20328 const ptr_val = field_attr_default_value_ptr.optionalValue(zcu) orelse break :d .none;
20329 const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType();
20330 const ptr_ty = try pt.singleConstPtrType(field_ty);
20331 const deref_val = try sema.pointerDeref(block, field_attrs_src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime(
20332 block,
20333 field_attrs_src,
20334 .{ .simple = .struct_field_default_value },
20335 );
20336 if (deref_val.canMutateComptimeVarState(zcu)) {
20337 return sema.failWithContainsReferenceToComptimeVar(block, field_attrs_src, field_name, "field default value", deref_val);
20338 }
20339 any_field_defaults = true;
20340 break :d deref_val.toIntern();
20341 };
20342
20343 if (field_attr_comptime.toBool()) {
20344 if (field_default == .none) {
20345 return sema.fail(block, field_attrs_src, "comptime field without default initialization value", .{});
20346 }
20347 if (layout != .auto) {
20348 return sema.fail(block, field_attrs_src, "{t} struct fields cannot be marked comptime", .{layout});
20349 }
20350 any_comptime_fields = true;
20351 }
20352
20353 if (field_attr_align.optionalValue(zcu)) |align_val| {
20354 if (layout == .@"packed") {
20355 return sema.fail(block, field_attrs_src, "packed struct fields cannot be aligned", .{});
20356 }
20357 // Trigger a compile error if the alignment is invalid.
20358 _ = try sema.validateAlign(block, field_attrs_src, align_val.toUnsignedInt(zcu));
20359 any_field_aligns = true;
20360 }
20361
20362 std.hash.autoHash(&hasher, .{
20363 field_name,
20364 field_attr_comptime,
20365 field_attr_align,
20366 field_default,
20367 });
20368 }
20369
20370 switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{
20371 .zir_index = tracked_inst,
20372 .type_hash = hasher.final(),
20373 .fields_len = @intCast(fields_len),
20374 .layout = layout,
20375 .any_comptime_fields = any_comptime_fields,
20376 .any_field_defaults = any_field_defaults,
20377 .any_field_aligns = any_field_aligns,
20378 .packed_backing_int_type = if (backing_int_ty) |ty| ty.toIntern() else .none,
20379 })) {
20380 .existing => |ty| {
20381 try sema.addTypeReferenceEntry(src, .fromInterned(ty));
20382 // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty.
20383 return .fromIntern(ty);
20384 },
20385 .wip => |wip| {
20386 errdefer wip.cancel(ip, pt.tid);
20387 try sema.setTypeName(block, &wip, name_strategy, "struct", inst);
20388 for (0..fields_len) |field_idx| {
20389 const field_name_val = try field_names_arr.elemValue(pt, field_idx);
20390 const field_attrs_val = try field_attrs_arr.elemValue(pt, field_idx);
20391
20392 // No source location or reason; first loop checked this is valid.
20393 const field_name = try sema.sliceToIpString(block, .unneeded, field_name_val, undefined);
20394 wip.field_names.get(ip)[field_idx] = field_name;
20395
20396 const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType();
20397 wip.field_types.get(ip)[field_idx] = field_ty.toIntern();
20398
20399 const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
20400 std.lang.Type.Struct.FieldAttributes,
20401 "comptime",
20402 ).?);
20403 const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
20404 std.lang.Type.Struct.FieldAttributes,
20405 "align",
20406 ).?);
20407 const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex(
20408 std.lang.Type.Struct.FieldAttributes,
20409 "default_value_ptr",
20410 ).?);
20411
20412 if (field_attr_comptime.toBool()) {
20413 const bit_bag_index = field_idx / 32;
20414 const mask = @as(u32, 1) << @intCast(field_idx % 32);
20415 wip.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask;
20416 }
20417
20418 if (field_attr_default_value_ptr.optionalValue(zcu)) |ptr_val| {
20419 const ptr_ty = try pt.singleConstPtrType(field_ty);
20420 // No source location; first loop checked this is valid.
20421 const deref_val = (try sema.pointerDeref(block, .unneeded, ptr_val, ptr_ty)).?;
20422 wip.field_values.get(ip)[field_idx] = deref_val.toIntern();
20423 } else if (any_field_defaults) {
20424 wip.field_values.get(ip)[field_idx] = .none;
20425 }
20426
20427 if (field_attr_align.optionalValue(zcu)) |field_align_val| {
20428 const bytes = field_align_val.toUnsignedInt(zcu);
20429 // No source location; first loop checked this is valid.
20430 const a = try sema.validateAlign(block, .unneeded, bytes);
20431 wip.field_aligns.get(ip)[field_idx] = a;
20432 } else if (any_field_aligns) {
20433 wip.field_aligns.get(ip)[field_idx] = .none;
20434 }
20435 }
20436
20437 const new_namespace_index = try pt.createNamespace(.{
20438 .parent = block.namespace.toOptional(),
20439 .owner_type = wip.index,
20440 .file_scope = block.getFileScopeIndex(zcu),
20441 .generation = zcu.generation,
20442 });
20443 errdefer pt.destroyNamespace(new_namespace_index);
20444 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
20445 try sema.addTypeReferenceEntry(src, .fromInterned(wip.index));
20446 return .fromIntern(wip.finish(ip, new_namespace_index));
20447 },
20448 }
20449}
20450
20451fn zirReifyUnion(
20452 sema: *Sema,
20453 block: *Block,
20454 extended: Zir.Inst.Extended.InstData,
20455 inst: Zir.Inst.Index,
20456) CompileError!Air.Inst.Ref {
20457 const pt = sema.pt;
20458 const zcu = pt.zcu;
20459 const comp = zcu.comp;
20460 const gpa = comp.gpa;
20461 const io = comp.io;
20462 const ip = &zcu.intern_pool;
20463
20464 const name_strategy: Zir.Inst.NameStrategy = @fromBackingInt(@intCast(extended.small));
20465 const extra = sema.code.extraData(Zir.Inst.ReifyUnion, extended.operand).data;
20466 const tracked_inst = try block.trackZir(inst);
20467 const src: LazySrcLoc = .{
20468 .base_node_inst = tracked_inst,
20469 .offset = .nodeOffset(.zero),
20470 };
20471
20472 const layout_src: LazySrcLoc = .{
20473 .base_node_inst = tracked_inst,
20474 .offset = .{ .node_offset_builtin_call_arg = .{
20475 .builtin_call_node = .zero,
20476 .arg_index = 0,
20477 } },
20478 };
20479 const arg_ty_src: LazySrcLoc = .{
20480 .base_node_inst = tracked_inst,
20481 .offset = .{ .node_offset_builtin_call_arg = .{
20482 .builtin_call_node = .zero,
20483 .arg_index = 1,
20484 } },
20485 };
20486 const field_names_src: LazySrcLoc = .{
20487 .base_node_inst = tracked_inst,
20488 .offset = .{ .node_offset_builtin_call_arg = .{
20489 .builtin_call_node = .zero,
20490 .arg_index = 2,
20491 } },
20492 };
20493 const field_types_src: LazySrcLoc = .{
20494 .base_node_inst = tracked_inst,
20495 .offset = .{ .node_offset_builtin_call_arg = .{
20496 .builtin_call_node = .zero,
20497 .arg_index = 3,
20498 } },
20499 };
20500 const field_attrs_src: LazySrcLoc = .{
20501 .base_node_inst = tracked_inst,
20502 .offset = .{ .node_offset_builtin_call_arg = .{
20503 .builtin_call_node = .zero,
20504 .arg_index = 4,
20505 } },
20506 };
20507
20508 const container_layout_ty = try sema.getStdLangType(layout_src, .@"Type.ContainerLayout");
20509 const single_field_attrs_ty = try sema.getStdLangType(field_attrs_src, .@"Type.Union.FieldAttributes");
20510
20511 const layout_uncoerced = sema.resolveInst(extra.layout);
20512 const layout_coerced = try sema.coerce(block, container_layout_ty, layout_uncoerced, layout_src);
20513 const layout_val = try sema.resolveConstDefinedValue(block, layout_src, layout_coerced, .{ .simple = .union_layout });
20514 const layout = try sema.interpretStdLangType(block, layout_src, layout_val, std.lang.Type.ContainerLayout);
20515
20516 const arg_ty_uncoerced = sema.resolveInst(extra.arg_ty);
20517 const arg_ty_coerced = try sema.coerce(block, .optional_type, arg_ty_uncoerced, arg_ty_src);
20518 const arg_ty_val = try sema.resolveConstDefinedValue(block, arg_ty_src, arg_ty_coerced, switch (layout) {
20519 .@"packed" => .{ .simple = .packed_union_backing_int_type },
20520 .auto, .@"extern" => .{ .simple = .union_enum_tag_type },
20521 });
20522
20523 const field_names_uncoerced = sema.resolveInst(extra.field_names);
20524 const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src);
20525 const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .union_field_names });
20526 const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .union_field_names });
20527
20528 const fields_len = try sema.usizeCast(block, src, field_names_arr.typeOf(zcu).arrayLen(zcu));
20529
20530 const field_types_ty = try pt.singleConstPtrType(try pt.arrayType(.{
20531 .len = fields_len,
20532 .child = .type_type,
20533 }));
20534 const field_attrs_ty = try pt.singleConstPtrType(try pt.arrayType(.{
20535 .len = fields_len,
20536 .child = single_field_attrs_ty.toIntern(),
20537 }));
20538
20539 const field_types_uncoerced = sema.resolveInst(extra.field_types);
20540 const field_types_coerced = try sema.coerce(block, field_types_ty, field_types_uncoerced, field_types_src);
20541 const field_types_slice = try sema.resolveConstDefinedValue(block, field_types_src, field_types_coerced, .{ .simple = .union_field_types });
20542 const field_types_arr = try sema.derefSliceAsArray(block, field_types_src, field_types_slice, .{ .simple = .union_field_types });
20543
20544 const field_attrs_uncoerced = sema.resolveInst(extra.field_attrs);
20545 const field_attrs_coerced = try sema.coerce(block, field_attrs_ty, field_attrs_uncoerced, field_attrs_src);
20546 const field_attrs_slice = try sema.resolveConstDefinedValue(block, field_attrs_src, field_attrs_coerced, .{ .simple = .union_field_attrs });
20547 const field_attrs_arr = try sema.derefSliceAsArray(block, field_attrs_src, field_attrs_slice, .{ .simple = .union_field_attrs });
20548
20549 // Before we begin, check for undefs...
20550 if (try sema.anyUndef(block, field_attrs_src, field_attrs_arr)) {
20551 return sema.failWithUseOfUndef(block, field_attrs_src, null);
20552 }
20553 if (try sema.anyUndef(block, field_types_src, field_types_arr)) {
20554 return sema.failWithUseOfUndef(block, field_types_src, null);
20555 }
20556 // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us.
20557 if (try sema.anyUndef(block, arg_ty_src, arg_ty_val)) {
20558 return sema.failWithUseOfUndef(block, arg_ty_src, null);
20559 }
20560
20561 // Most validation of this type happens during type resolution. We basically need to do the work
20562 // which AstGen would normally do. An exception is checking for duplicate field names, which is
20563 // handled by type resolution---it just simplifies some logic a little.
20564
20565 // As well as validation, we're going to gather some information about the fields, and construct
20566 // a hash representing the inputs for deduplication purposes.
20567
20568 var any_field_aligns = false;
20569
20570 // TODO: use a longer hash!
20571 var hasher = std.hash.Wyhash.init(0);
20572 std.hash.autoHash(&hasher, layout);
20573 std.hash.autoHash(&hasher, arg_ty_val);
20574
20575 const explicit_tag_ty: ?Type, const explicit_packed_backing_type: ?Type = ty: {
20576 const arg_ty = arg_ty_val.optionalValue(zcu) orelse break :ty .{ null, null };
20577 switch (layout) {
20578 .@"extern" => return sema.fail(block, arg_ty_src, "extern union does not support enum tag type", .{}),
20579 .@"packed" => break :ty .{ null, arg_ty.toType() },
20580 .auto => break :ty .{ arg_ty.toType(), null },
20581 }
20582 };
20583
20584 // `field_types_arr` and `field_attrs_arr` are already deduplicated by the InternPool!
20585 std.hash.autoHash(&hasher, field_types_arr);
20586 std.hash.autoHash(&hasher, field_attrs_arr);
20587 // However, for field names, we need to iterate the individual fields, because the pointers (the
20588 // names are slices) mean that distinct values could ultimately result in the same union type.
20589 for (0..fields_len) |field_idx| {
20590 const field_name_val = try field_names_arr.elemValue(pt, field_idx);
20591 const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, .{ .simple = .union_field_names });
20592 std.hash.autoHash(&hasher, field_name);
20593
20594 const field_attrs = try sema.interpretStdLangType(
20595 block,
20596 field_attrs_src,
20597 try field_attrs_arr.elemValue(pt, field_idx),
20598 std.lang.Type.Union.FieldAttributes,
20599 );
20600 if (field_attrs.@"align") |bytes| {
20601 if (layout == .@"packed") {
20602 return sema.fail(block, field_attrs_src, "packed union fields cannot be aligned", .{});
20603 }
20604 // Trigger a compile error if the alignment is invalid.
20605 _ = try sema.validateAlign(block, field_attrs_src, bytes);
20606 any_field_aligns = true;
20607 }
20608 }
20609
20610 switch (try ip.getReifiedUnionType(gpa, io, pt.tid, .{
20611 .zir_index = tracked_inst,
20612 .type_hash = hasher.final(),
20613 .fields_len = @intCast(fields_len),
20614 .layout = layout,
20615 .any_field_aligns = any_field_aligns,
20616 .tag_usage = tag: {
20617 if (explicit_tag_ty != null) break :tag .tagged;
20618 if (layout == .auto and block.wantSafeTypes()) break :tag .safety;
20619 break :tag .none;
20620 },
20621 .enum_tag_type = if (explicit_tag_ty) |ty| ty.toIntern() else .none,
20622 .packed_backing_int_type = if (explicit_packed_backing_type) |ty| ty.toIntern() else .none,
20623 })) {
20624 .existing => |ty| {
20625 try sema.addTypeReferenceEntry(src, .fromInterned(ty));
20626 // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty.
20627 return .fromIntern(ty);
20628 },
20629 .wip => |wip| {
20630 errdefer wip.cancel(ip, pt.tid);
20631 try sema.setTypeName(block, &wip, name_strategy, "union", inst);
20632
20633 for (0..fields_len) |field_idx| {
20634 const field_name_val = try field_names_arr.elemValue(pt, field_idx);
20635 // No source location or reason; first loop checked this is valid.
20636 const field_name = try sema.sliceToIpString(block, .unneeded, field_name_val, undefined);
20637 wip.field_names.get(ip)[field_idx] = field_name;
20638
20639 const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType();
20640 wip.field_types.get(ip)[field_idx] = field_ty.toIntern();
20641
20642 // No source location; first loop checked this is valid.
20643 const field_attrs = try sema.interpretStdLangType(
20644 block,
20645 .unneeded,
20646 try field_attrs_arr.elemValue(pt, field_idx),
20647 std.lang.Type.Union.FieldAttributes,
20648 );
20649 if (field_attrs.@"align") |bytes| {
20650 // No source location; first loop checked this is valid.
20651 const a = try sema.validateAlign(block, .unneeded, bytes);
20652 wip.field_aligns.get(ip)[field_idx] = a;
20653 } else if (any_field_aligns) {
20654 wip.field_aligns.get(ip)[field_idx] = .none;
20655 }
20656 }
20657
20658 const new_namespace_index = try pt.createNamespace(.{
20659 .parent = block.namespace.toOptional(),
20660 .owner_type = wip.index,
20661 .file_scope = block.getFileScopeIndex(zcu),
20662 .generation = zcu.generation,
20663 });
20664 errdefer pt.destroyNamespace(new_namespace_index);
20665 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
20666 try sema.addTypeReferenceEntry(src, .fromInterned(wip.index));
20667 return .fromIntern(wip.finish(ip, new_namespace_index));
20668 },
20669 }
20670}
20671
20672fn zirReifyEnum(
20673 sema: *Sema,
20674 block: *Block,
20675 extended: Zir.Inst.Extended.InstData,
20676 inst: Zir.Inst.Index,
20677) CompileError!Air.Inst.Ref {
20678 const pt = sema.pt;
20679 const zcu = pt.zcu;
20680 const comp = zcu.comp;
20681 const gpa = comp.gpa;
20682 const io = comp.io;
20683 const ip = &zcu.intern_pool;
20684
20685 const name_strategy: Zir.Inst.NameStrategy = @fromBackingInt(@intCast(extended.small));
20686 const extra = sema.code.extraData(Zir.Inst.ReifyEnum, extended.operand).data;
20687 const tracked_inst = try block.trackZir(inst);
20688 const src: LazySrcLoc = .{
20689 .base_node_inst = tracked_inst,
20690 .offset = .nodeOffset(.zero),
20691 };
20692
20693 const tag_ty_src: LazySrcLoc = .{
20694 .base_node_inst = tracked_inst,
20695 .offset = .{ .node_offset_builtin_call_arg = .{
20696 .builtin_call_node = .zero,
20697 .arg_index = 0,
20698 } },
20699 };
20700 const mode_src: LazySrcLoc = .{
20701 .base_node_inst = tracked_inst,
20702 .offset = .{ .node_offset_builtin_call_arg = .{
20703 .builtin_call_node = .zero,
20704 .arg_index = 1,
20705 } },
20706 };
20707 const field_names_src: LazySrcLoc = .{
20708 .base_node_inst = tracked_inst,
20709 .offset = .{ .node_offset_builtin_call_arg = .{
20710 .builtin_call_node = .zero,
20711 .arg_index = 2,
20712 } },
20713 };
20714 const field_values_src: LazySrcLoc = .{
20715 .base_node_inst = tracked_inst,
20716 .offset = .{ .node_offset_builtin_call_arg = .{
20717 .builtin_call_node = .zero,
20718 .arg_index = 3,
20719 } },
20720 };
20721
20722 const enum_mode_ty = try sema.getStdLangType(mode_src, .@"Type.Enum.Mode");
20723
20724 const tag_ty_uncoerced = sema.resolveInst(extra.tag_ty);
20725 const tag_ty_coerced = try sema.coerce(block, .type, tag_ty_uncoerced, tag_ty_src);
20726 const tag_ty_val = try sema.resolveConstDefinedValue(block, tag_ty_src, tag_ty_coerced, .{ .simple = .enum_int_tag_type });
20727 const tag_ty = tag_ty_val.toType();
20728
20729 const mode_uncoerced = sema.resolveInst(extra.mode);
20730 const mode_coerced = try sema.coerce(block, enum_mode_ty, mode_uncoerced, mode_src);
20731 const mode_val = try sema.resolveConstDefinedValue(block, mode_src, mode_coerced, .{ .simple = .type });
20732 const nonexhaustive = switch (try sema.interpretStdLangType(block, mode_src, mode_val, std.lang.Type.Enum.Mode)) {
20733 .exhaustive => false,
20734 .nonexhaustive => true,
20735 };
20736
20737 const field_names_uncoerced = sema.resolveInst(extra.field_names);
20738 const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src);
20739 const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .enum_field_names });
20740 const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .enum_field_names });
20741
20742 const fields_len = try sema.usizeCast(block, src, field_names_arr.typeOf(zcu).arrayLen(zcu));
20743
20744 const field_values_ty = try pt.singleConstPtrType(try pt.arrayType(.{
20745 .len = fields_len,
20746 .child = tag_ty.toIntern(),
20747 }));
20748
20749 const field_values_uncoerced = sema.resolveInst(extra.field_values);
20750 const field_values_coerced = try sema.coerce(block, field_values_ty, field_values_uncoerced, field_values_src);
20751 const field_values_slice = try sema.resolveConstDefinedValue(block, field_values_src, field_values_coerced, .{ .simple = .enum_field_values });
20752 const field_values_arr = try sema.derefSliceAsArray(block, field_values_src, field_values_slice, .{ .simple = .enum_field_values });
20753
20754 // Before we begin, check for undefs...
20755 if (try sema.anyUndef(block, field_values_src, field_values_arr)) {
20756 return sema.failWithUseOfUndef(block, field_values_src, null);
20757 }
20758 // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us.
20759
20760 // Most validation of this type happens during type resolution. We basically need to do the work
20761 // which AstGen would normally do. An exception is checking for duplicate field names, which is
20762 // handled by type resolution---it just simplifies some logic a little.
20763
20764 // As well as validation, we're going to gather some information about the fields, and construct
20765 // a hash representing the inputs for deduplication purposes.
20766
20767 // TODO: use a longer hash!
20768 var hasher = std.hash.Wyhash.init(0);
20769 std.hash.autoHash(&hasher, tag_ty.toIntern());
20770 std.hash.autoHash(&hasher, nonexhaustive);
20771 std.hash.autoHash(&hasher, fields_len);
20772 // `field_values_arr` is already deduplicated by the InternPool!
20773 std.hash.autoHash(&hasher, field_values_arr);
20774 // However, for field names, we need to iterate the individual fields, because the pointers (the
20775 // names are slices) mean that distinct values could ultimately result in the same enum type.
20776 for (0..fields_len) |field_idx| {
20777 const field_name_val = try field_names_arr.elemValue(pt, field_idx);
20778 const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, .{ .simple = .enum_field_names });
20779 std.hash.autoHash(&hasher, field_name);
20780 }
20781
20782 switch (try ip.getReifiedEnumType(gpa, io, pt.tid, .{
20783 .zir_index = tracked_inst,
20784 .type_hash = hasher.final(),
20785 .fields_len = @intCast(fields_len),
20786 .nonexhaustive = nonexhaustive,
20787 .int_tag_type = tag_ty.toIntern(),
20788 })) {
20789 .existing => |ty| {
20790 try sema.addTypeReferenceEntry(src, .fromInterned(ty));
20791 // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty.
20792 return .fromIntern(ty);
20793 },
20794 .wip => |wip| {
20795 errdefer wip.cancel(ip, pt.tid);
20796
20797 try sema.setTypeName(block, &wip, name_strategy, "enum", inst);
20798
20799 // Populate field names and values. Duplicate checking will be handled by type resolution.
20800 for (0..fields_len) |field_index| {
20801 const field_name_val = try field_names_arr.elemValue(pt, field_index);
20802 // No source location or reason; first loop checked this is valid.
20803 const field_name = try sema.sliceToIpString(block, .unneeded, field_name_val, undefined);
20804 wip.field_names.get(ip)[field_index] = field_name;
20805
20806 const field_val = try field_values_arr.elemValue(pt, field_index);
20807 wip.field_values.get(ip)[field_index] = field_val.toIntern();
20808 }
20809
20810 const new_namespace_index = try pt.createNamespace(.{
20811 .parent = block.namespace.toOptional(),
20812 .owner_type = wip.index,
20813 .file_scope = block.getFileScopeIndex(zcu),
20814 .generation = zcu.generation,
20815 });
20816 errdefer pt.destroyNamespace(new_namespace_index);
20817 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
20818 try sema.addTypeReferenceEntry(src, .fromInterned(wip.index));
20819 return .fromIntern(wip.finish(ip, new_namespace_index));
20820 },
20821 }
20822}
20823
20824fn zirReifySpirvType(
20825 sema: *Sema,
20826 block: *Block,
20827 extended: Zir.Inst.Extended.InstData,
20828 inst: Zir.Inst.Index,
20829) CompileError!Air.Inst.Ref {
20830 const pt = sema.pt;
20831 const zcu = pt.zcu;
20832 const comp = zcu.comp;
20833 const gpa = comp.gpa;
20834 const io = comp.io;
20835 const ip = &zcu.intern_pool;
20836 const target = zcu.getTarget();
20837
20838 const extra = sema.code.extraData(Zir.Inst.ReifySpirvType, extended.operand).data;
20839 const tracked_inst = try block.trackZir(inst);
20840 const src: LazySrcLoc = .{
20841 .base_node_inst = tracked_inst,
20842 .offset = .nodeOffset(.zero),
20843 };
20844 const operand_src: LazySrcLoc = .{
20845 .base_node_inst = tracked_inst,
20846 .offset = .{ .node_offset_builtin_call_arg = .{
20847 .builtin_call_node = .zero,
20848 .arg_index = 0,
20849 } },
20850 };
20851
20852 if (!target.cpu.arch.isSpirV()) {
20853 return sema.fail(
20854 block,
20855 src,
20856 "builtin @SpirvType is only available when targeting SPIR-V; targeted CPU architecture is {t}",
20857 .{target.cpu.arch},
20858 );
20859 }
20860
20861 const spirv_type_options_ty = try sema.getStdLangType(operand_src, .@"Type.Spirv");
20862 const operand_uncoerced = sema.resolveInst(extra.operand);
20863 const operand_coerced = try sema.coerce(block, spirv_type_options_ty, operand_uncoerced, operand_src);
20864 const operand_val = try sema.resolveConstDefinedValue(block, operand_src, operand_coerced, .{ .simple = .type });
20865 const union_val = ip.indexToKey(operand_val.toIntern()).un;
20866
20867 if (try sema.anyUndef(block, operand_src, .fromInterned(union_val.val))) {
20868 return sema.failWithUseOfUndef(block, operand_src, null);
20869 }
20870
20871 const tag = try sema.interpretStdLangType(block, src, .fromInterned(union_val.tag), @typeInfo(std.lang.Type.Spirv).@"union".tag_type.?);
20872 const ip_data: InternPool.Key.SpirvType = switch (tag) {
20873 .sampler => .{
20874 .ty = .none,
20875 .flags = .{
20876 .tag = .sampler,
20877 .usage = .unknown,
20878 .format = .unknown,
20879 .dim = .@"1d",
20880 .depth = .unknown,
20881 .access = .unknown,
20882 .is_arrayed = false,
20883 .is_multisampled = false,
20884 },
20885 },
20886 .image => ip_data: {
20887 const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
20888 const usage_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20889 ip,
20890 try ip.getOrPutString(gpa, io, pt.tid, "usage", .no_embedded_nulls),
20891 ).?);
20892 const format_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20893 ip,
20894 try ip.getOrPutString(gpa, io, pt.tid, "format", .no_embedded_nulls),
20895 ).?);
20896 const dim_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20897 ip,
20898 try ip.getOrPutString(gpa, io, pt.tid, "dim", .no_embedded_nulls),
20899 ).?);
20900 const depth_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20901 ip,
20902 try ip.getOrPutString(gpa, io, pt.tid, "depth", .no_embedded_nulls),
20903 ).?);
20904 const access_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20905 ip,
20906 try ip.getOrPutString(gpa, io, pt.tid, "access", .no_embedded_nulls),
20907 ).?);
20908 const arrayed_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20909 ip,
20910 try ip.getOrPutString(gpa, io, pt.tid, "arrayed", .no_embedded_nulls),
20911 ).?);
20912 const multisampled_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex(
20913 ip,
20914 try ip.getOrPutString(gpa, io, pt.tid, "multisampled", .no_embedded_nulls),
20915 ).?);
20916 const format = try sema.interpretStdLangType(block, operand_src, format_val, std.lang.Type.Spirv.Image.Format);
20917 const dim = try sema.interpretStdLangType(block, operand_src, dim_val, std.lang.Type.Spirv.Image.Dimensionality);
20918 const depth = try sema.interpretStdLangType(block, operand_src, depth_val, std.lang.Type.Spirv.Image.Depth);
20919 const access = try sema.interpretStdLangType(block, operand_src, access_val, std.lang.Type.Spirv.Image.Access);
20920
20921 switch (target.os.tag) {
20922 .opencl => if (access == .unknown) {
20923 return sema.fail(block, operand_src, "'access' field must be specified under the 'opencl' os", .{});
20924 },
20925 else => if (access != .unknown) {
20926 return sema.fail(block, operand_src, "access qualifier '.{t}' is only valid under the 'opencl' os", .{access});
20927 },
20928 }
20929
20930 const arrayed = try sema.interpretStdLangType(block, operand_src, arrayed_val, bool);
20931 const multisampled = try sema.interpretStdLangType(block, operand_src, multisampled_val, bool);
20932
20933 const usage_tag_val = usage_val.unionTag(zcu).?;
20934 const usage_tag = try sema.interpretStdLangType(block, operand_src, usage_tag_val, @typeInfo(std.lang.Type.Spirv.Image.Usage).@"union".tag_type.?);
20935
20936 switch (target.os.tag) {
20937 .vulkan => {
20938 if (usage_tag == .unknown) {
20939 return sema.fail(
20940 block,
20941 operand_src,
20942 "'usage' must be '.sampled' or '.storage' under the 'vulkan' os (Sampled == 0 is forbidden)",
20943 .{},
20944 );
20945 }
20946 },
20947 .opencl => {
20948 if (usage_tag != .unknown) {
20949 return sema.fail(block, operand_src, "'usage' must be '.unknown' under the 'opencl' os", .{});
20950 }
20951 if (multisampled) {
20952 return sema.fail(block, operand_src, "'multisampled' must be 'false' under the 'opencl' os", .{});
20953 }
20954 if (format != .unknown) {
20955 return sema.fail(block, operand_src, "'format' must be '.unknown' under the 'opencl' os", .{});
20956 }
20957 if (dim == .cube) {
20958 return sema.fail(block, operand_src, "'dim' '.cube' is not allowed under the 'opencl' os", .{});
20959 }
20960 if (arrayed and dim != .@"1d" and dim != .@"2d") {
20961 return sema.fail(block, operand_src, "'arrayed' may only be 'true' when 'dim' is '.1d' or '.2d' under the 'opencl' os", .{});
20962 }
20963 },
20964 else => {},
20965 }
20966
20967 break :ip_data .{
20968 .ty = blk: {
20969 const sampled_type = usage_val.unionPayload(zcu).toType();
20970
20971 if (target.os.tag != .opencl and sampled_type.toIntern() == .void_type) {
20972 return sema.fail(block, operand_src, "'void' type for '{t}' field is only valid under the 'opencl' os", .{usage_tag});
20973 }
20974 if (target.os.tag == .opencl and sampled_type.toIntern() != .void_type) {
20975 return sema.fail(block, operand_src, "'{t}' field type must be 'void' under the 'opencl' os", .{usage_tag});
20976 }
20977
20978 if (sampled_type.toIntern() != .void_type and
20979 (!sampled_type.hasRuntimeBits(zcu) or (!sampled_type.isRuntimeFloat() and !sampled_type.isInt(zcu))))
20980 {
20981 return sema.fail(block, operand_src, "invalid '{t}' field value '{f}'", .{ usage_tag, sampled_type.fmt(pt) });
20982 }
20983
20984 if (target.os.tag == .vulkan) {
20985 const ok = (sampled_type.isRuntimeFloat() and sampled_type.bitSize(zcu) == 32) or
20986 (sampled_type.isInt(zcu) and (sampled_type.bitSize(zcu) == 32 or sampled_type.bitSize(zcu) == 64));
20987 if (!ok) {
20988 return sema.fail(
20989 block,
20990 operand_src,
20991 "'{t}' field value must be a 32-bit int, 64-bit int or 32-bit float under the 'vulkan' os",
20992 .{usage_tag},
20993 );
20994 }
20995
20996 if (format != .unknown) {
20997 const format_kind: enum { float, sint, uint } = switch (format) {
20998 .rgba32f, .rgba16f, .rgba8unorm, .rgba8snorm, .r32f => .float,
20999 .rgba32i, .rgba16i, .rgba8i, .r32i => .sint,
21000 .rgba32u, .rgba16u, .rgba8u, .r32u => .uint,
21001 .unknown => unreachable,
21002 };
21003 const matches = switch (format_kind) {
21004 .float => sampled_type.isRuntimeFloat(),
21005 .sint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .signed,
21006 .uint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .unsigned,
21007 };
21008 if (!matches) {
21009 return sema.fail(
21010 block,
21011 operand_src,
21012 "image 'format' '.{t}' does not match '{t}' type '{f}' under the 'vulkan' os",
21013 .{ format, usage_tag, sampled_type.fmt(pt) },
21014 );
21015 }
21016 }
21017 }
21018
21019 break :blk sampled_type.toIntern();
21020 },
21021 .flags = .{
21022 .tag = .image,
21023 .usage = usage_tag,
21024 .format = format,
21025 .dim = dim,
21026 .depth = depth,
21027 .access = access,
21028 .is_arrayed = arrayed,
21029 .is_multisampled = multisampled,
21030 },
21031 };
21032 },
21033 .sampled_image => blk: {
21034 const image_ty = Value.fromInterned(union_val.val).toType();
21035 if (image_ty.zigTypeTag(zcu) != .spirv or ip.loadSpirvType(image_ty.toIntern()).flags.tag != .image) {
21036 return sema.fail(block, operand_src, "'sampled_image' element must be an @SpirvType image, found '{f}'", .{image_ty.fmt(pt)});
21037 }
21038 const image_info = ip.loadSpirvType(image_ty.toIntern()).flags;
21039 if (image_info.usage != .sampled) {
21040 return sema.fail(block, operand_src, "'sampled_image' element must be an image with 'usage = .sampled'", .{});
21041 }
21042 break :blk .{
21043 .ty = union_val.val,
21044 .flags = .{
21045 .tag = tag,
21046 .usage = .unknown,
21047 .format = .unknown,
21048 .dim = .@"1d",
21049 .depth = .unknown,
21050 .access = .unknown,
21051 .is_arrayed = false,
21052 .is_multisampled = false,
21053 },
21054 };
21055 },
21056 .runtime_array => blk: {
21057 const elem_ty = Value.fromInterned(union_val.val).toType();
21058 if (elem_ty.toIntern() == .void_type) {
21059 return sema.fail(block, operand_src, "'runtime_array' element type must not be 'void'", .{});
21060 }
21061 if (target.os.tag == .vulkan and
21062 elem_ty.zigTypeTag(zcu) == .spirv and
21063 ip.loadSpirvType(elem_ty.toIntern()).flags.tag == .runtime_array)
21064 {
21065 return sema.fail(block, operand_src, "'runtime_array' of 'runtime_array' is not allowed under the 'vulkan' os", .{});
21066 }
21067 break :blk .{
21068 .ty = union_val.val,
21069 .flags = .{
21070 .tag = tag,
21071 .usage = .unknown,
21072 .format = .unknown,
21073 .dim = .@"1d",
21074 .depth = .unknown,
21075 .access = .unknown,
21076 .is_arrayed = false,
21077 .is_multisampled = false,
21078 },
21079 };
21080 },
21081 };
21082
21083 return .fromIntern(try ip.getReifiedSpirvType(gpa, io, pt.tid, ip_data));
21084}
21085
21086fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
21087 const pt = sema.pt;
21088 const va_list_ty = try sema.getStdLangType(src, .VaList);
21089 const va_list_ptr = try pt.singleMutPtrType(va_list_ty);
21090
21091 const inst = sema.resolveInst(zir_ref);
21092 return sema.coerce(block, va_list_ptr, inst, src);
21093}
21094
21095fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
21096 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
21097 const src = block.nodeOffset(extra.node);
21098 const va_list_src = block.builtinCallArgSrc(extra.node, 0);
21099 const ty_src = block.builtinCallArgSrc(extra.node, 1);
21100
21101 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs);
21102 const arg_ty = try sema.resolveType(block, ty_src, extra.rhs);
21103 try sema.ensureLayoutResolved(arg_ty, ty_src, .parameter);
21104 if (!arg_ty.validateExtern(.param_ty, sema.pt.zcu)) {
21105 const msg = msg: {
21106 const msg = try sema.errMsg(ty_src, "cannot get '{f}' from variadic argument", .{arg_ty.fmt(sema.pt)});
21107 errdefer msg.destroy(sema.gpa);
21108
21109 try sema.explainWhyTypeIsNotExtern(msg, ty_src, arg_ty, .param_ty);
21110
21111 try sema.addDeclaredHereNote(msg, arg_ty);
21112 break :msg msg;
21113 };
21114 return sema.failWithOwnedErrorMsg(block, msg);
21115 }
21116
21117 try sema.requireRuntimeBlock(block, src, null);
21118 return block.addTyOp(.c_va_arg, arg_ty, va_list_ref);
21119}
21120
21121fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
21122 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
21123 const src = block.nodeOffset(extra.node);
21124 const va_list_src = block.builtinCallArgSrc(extra.node, 0);
21125
21126 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
21127 const va_list_ty = try sema.getStdLangType(src, .VaList);
21128
21129 try sema.requireRuntimeBlock(block, src, null);
21130 return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref);
21131}
21132
21133fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
21134 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
21135 const src = block.nodeOffset(extra.node);
21136 const va_list_src = block.builtinCallArgSrc(extra.node, 0);
21137
21138 const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand);
21139
21140 try sema.requireRuntimeBlock(block, src, null);
21141 _ = try block.addUnOp(.c_va_end, va_list_ref);
21142 return .void_value;
21143}
21144
21145fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
21146 const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand))));
21147 const src = block.nodeOffset(src_node);
21148
21149 const va_list_ty = try sema.getStdLangType(src, .VaList);
21150 try sema.requireRuntimeBlock(block, src, null);
21151 return block.addInst(.{
21152 .tag = .c_va_start,
21153 .data = .{ .ty = va_list_ty },
21154 });
21155}
21156
21157fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
21158 const pt = sema.pt;
21159 const zcu = pt.zcu;
21160 const comp = zcu.comp;
21161 const gpa = comp.gpa;
21162 const io = comp.io;
21163 const ip = &zcu.intern_pool;
21164
21165 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
21166 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
21167 const ty = try sema.resolveType(block, ty_src, inst_data.operand);
21168
21169 const type_name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}", .{ty.fmt(pt)}, .no_embedded_nulls);
21170 return sema.addNullTerminatedStrLit(type_name);
21171}
21172
21173fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
21174 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
21175 const src = block.nodeOffset(inst_data.src_node);
21176 return sema.failWithUseOfAsync(block, src);
21177}
21178
21179fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
21180 const pt = sema.pt;
21181 const zcu = pt.zcu;
21182 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
21183 const src = block.nodeOffset(inst_data.src_node);
21184 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
21185 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
21186 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat");
21187 const operand = sema.resolveInst(extra.rhs);
21188 const operand_ty = sema.typeOf(operand);
21189
21190 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
21191 const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
21192
21193 const dest_scalar_ty = dest_ty.scalarType(zcu);
21194 const operand_scalar_ty = operand_ty.scalarType(zcu);
21195
21196 switch (dest_scalar_ty.zigTypeTag(zcu)) {
21197 .comptime_int, .int => {},
21198 else => return sema.fail(block, src, "expected integer result type, found '{f}'", .{dest_scalar_ty.fmt(pt)}),
21199 }
21200 try sema.checkFloatType(block, operand_src, operand_scalar_ty);
21201
21202 if (sema.resolveValue(operand)) |operand_val| {
21203 const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate);
21204 return Air.internedToRef(result_val.toIntern());
21205 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
21206 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int });
21207 }
21208
21209 try sema.requireRuntimeBlock(block, src, operand_src);
21210 if (dest_scalar_ty.toIntern() == .u0_type) {
21211 if (block.wantSafety()) {
21212 // Emit an explicit safety check. We can do this one like `abs(x) < 1`.
21213 const abs_ref = try block.addTyOp(.abs, operand_ty, operand);
21214 const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref;
21215 const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern());
21216 const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref);
21217 try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds);
21218 }
21219 const scalar_val = try pt.intValue(dest_scalar_ty, 0);
21220 return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern());
21221 }
21222 if (block.wantSafety()) {
21223 try sema.preparePanicId(src, .integer_part_out_of_bounds);
21224 return block.addTyOp(switch (block.float_mode) {
21225 .optimized => .int_from_float_optimized_safe,
21226 .strict => .int_from_float_safe,
21227 }, dest_ty, operand);
21228 }
21229 return block.addTyOp(switch (block.float_mode) {
21230 .optimized => .int_from_float_optimized,
21231 .strict => .int_from_float,
21232 }, dest_ty, operand);
21233}
21234
21235fn zirRoundCast(
21236 sema: *Sema,
21237 block: *Block,
21238 extended: Zir.Inst.Extended.InstData,
21239) CompileError!Air.Inst.Ref {
21240 const pt = sema.pt;
21241 const zcu = pt.zcu;
21242 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
21243 const src = block.nodeOffset(extra.node);
21244 const operand_src = block.builtinCallArgSrc(extra.node, 0);
21245
21246 const operand = sema.resolveInst(extra.rhs);
21247
21248 const round_op: Zir.Inst.RoundOp = @fromBackingInt(@intCast(extended.small));
21249 const mode: IntFromFloatMode = switch (round_op) {
21250 .round => .round,
21251 .floor => .floor,
21252 .ceil => .ceil,
21253 .trunc => .truncate,
21254 };
21255
21256 const dest_ty = (try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse switch (mode) {
21257 // zig fmt: off
21258 .round => return sema.unaryMath(block, operand_src, operand, .round, Value.round),
21259 .floor => return sema.unaryMath(block, operand_src, operand, .floor, Value.floor),
21260 .ceil => return sema.unaryMath(block, operand_src, operand, .ceil, Value.ceil),
21261 .truncate => return sema.unaryMath(block, operand_src, operand, .trunc_float, Value.trunc),
21262 // zig fmt: on
21263 .exact => unreachable,
21264 }).optEuBaseType(zcu);
21265
21266 const operand_ty = sema.typeOf(operand);
21267
21268 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
21269
21270 const dest_scalar_ty = dest_ty.scalarType(zcu);
21271 const operand_scalar_ty = operand_ty.scalarType(zcu);
21272
21273 switch (operand_scalar_ty.zigTypeTag(zcu)) {
21274 .comptime_float, .float => {},
21275 else => return sema.fail(
21276 block,
21277 operand_src,
21278 "expected float or vector type, found '{f}'",
21279 .{operand_ty.fmt(pt)},
21280 ),
21281 }
21282
21283 switch (dest_scalar_ty.zigTypeTag(zcu)) {
21284 .float, .comptime_float => {
21285 const coerced_operand = try sema.coerce(block, dest_ty, operand, operand_src);
21286
21287 const result_ref = switch (mode) {
21288 .round => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.round),
21289 .floor => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.floor),
21290 .ceil => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.ceil),
21291 .truncate => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.trunc),
21292 .exact => unreachable,
21293 };
21294
21295 if (result_ref) |ref| return ref;
21296
21297 const air_tag: Air.Inst.Tag = switch (mode) {
21298 .round => .round,
21299 .floor => .floor,
21300 .ceil => .ceil,
21301 .truncate => .trunc_float,
21302 .exact => unreachable,
21303 };
21304
21305 try sema.requireRuntimeBlock(block, operand_src, null);
21306 return block.addUnOp(air_tag, coerced_operand);
21307 },
21308 .int, .comptime_int => {},
21309 else => return sema.fail(
21310 block,
21311 src,
21312 "expected integer, float, or vector of either integers or floats, found '{f}'",
21313 .{dest_ty.fmt(pt)},
21314 ),
21315 }
21316
21317 if (sema.resolveValue(operand)) |operand_val| {
21318 const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, mode);
21319 return .fromValue(result_val);
21320 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
21321 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int });
21322 }
21323
21324 try sema.requireRuntimeBlock(block, src, operand_src);
21325
21326 if (dest_scalar_ty.toIntern() == .u0_type) {
21327 if (block.wantSafety()) {
21328 const abs_ref = try block.addTyOp(.abs, operand_ty, operand);
21329 const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
21330 const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref;
21331 const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern());
21332 const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref);
21333 try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds);
21334 }
21335 const scalar_val = try pt.intValue(dest_scalar_ty, 0);
21336 return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern());
21337 }
21338
21339 const safe = block.wantSafety();
21340
21341 if (safe) {
21342 try sema.preparePanicId(src, .integer_part_out_of_bounds);
21343 }
21344
21345 const uncasted_result: Air.Inst.Ref = switch (mode) {
21346 .truncate => operand,
21347 .round => try block.addUnOp(.round, operand),
21348 .floor => try block.addUnOp(.floor, operand),
21349 .ceil => try block.addUnOp(.ceil, operand),
21350 .exact => unreachable,
21351 };
21352 const air_cast_tag: Air.Inst.Tag = switch (block.float_mode) {
21353 .optimized => if (safe) .int_from_float_optimized_safe else .int_from_float_optimized,
21354 .strict => if (safe) .int_from_float_safe else .int_from_float,
21355 };
21356 return block.addTyOp(air_cast_tag, dest_ty, uncasted_result);
21357}
21358
21359fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
21360 const pt = sema.pt;
21361 const zcu = pt.zcu;
21362 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
21363 const src = block.nodeOffset(inst_data.src_node);
21364 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
21365 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
21366 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt");
21367 const operand = sema.resolveInst(extra.rhs);
21368 const operand_ty = sema.typeOf(operand);
21369
21370 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src);
21371
21372 const dest_scalar_ty = dest_ty.scalarType(zcu);
21373 const operand_scalar_ty = operand_ty.scalarType(zcu);
21374
21375 switch (dest_scalar_ty.zigTypeTag(zcu)) {
21376 .comptime_float, .float => {},
21377 else => return sema.fail(block, src, "expected float result type, found '{f}'", .{dest_scalar_ty.fmt(pt)}),
21378 }
21379 _ = try sema.checkIntType(block, operand_src, operand_scalar_ty);
21380
21381 if (sema.resolveValue(operand)) |operand_val| {
21382 if (operand_val.isUndef(zcu)) return .fromValue(try pt.undefValue(dest_ty));
21383 if (dest_ty.zigTypeTag(zcu) != .vector) {
21384 return .fromValue(try pt.floatValue(dest_ty, operand_val.toFloat(f128, zcu)));
21385 }
21386 const dest_elems = try sema.arena.alloc(InternPool.Index, dest_ty.vectorLen(zcu));
21387 for (dest_elems, 0..) |*out_elem, elem_idx| {
21388 const orig_elem = try operand_val.elemValue(pt, elem_idx);
21389 const casted_elem = if (orig_elem.isUndef(zcu))
21390 try pt.undefValue(dest_scalar_ty)
21391 else
21392 try pt.floatValue(dest_scalar_ty, orig_elem.toFloat(f128, zcu));
21393 out_elem.* = casted_elem.toIntern();
21394 }
21395 return .fromValue(try pt.aggregateValue(dest_ty, dest_elems));
21396 } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) {
21397 return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_float });
21398 }
21399
21400 try sema.requireRuntimeBlock(block, src, operand_src);
21401 return block.addTyOp(.float_from_int, dest_ty, operand);
21402}
21403
21404fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
21405 const pt = sema.pt;
21406 const zcu = pt.zcu;
21407 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
21408 const src = block.nodeOffset(inst_data.src_node);
21409
21410 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
21411
21412 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
21413 const operand_res = sema.resolveInst(extra.rhs);
21414
21415 const uncoerced_operand_ty = sema.typeOf(operand_res);
21416 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt");
21417 try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, uncoerced_operand_ty, src, operand_src);
21418
21419 const is_vector = dest_ty.zigTypeTag(zcu) == .vector;
21420 const operand_ty: Type = if (is_vector) operand_ty: {
21421 const len = dest_ty.vectorLen(zcu);
21422 break :operand_ty try pt.vectorType(.{ .child = .usize_type, .len = len });
21423 } else .usize;
21424
21425 const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src);
21426
21427 const ptr_ty = dest_ty.scalarType(zcu);
21428 try sema.checkPtrType(block, src, ptr_ty, true);
21429
21430 const elem_ty = ptr_ty.nullablePtrElem(zcu);
21431
21432 try sema.ensureLayoutResolved(elem_ty, src, .align_check);
21433 const ptr_align = ptr_ty.ptrAlignment(zcu);
21434
21435 if (ptr_ty.isSlice(zcu)) {
21436 const msg = msg: {
21437 const msg = try sema.errMsg(src, "integer cannot be converted to slice type '{f}'", .{ptr_ty.fmt(pt)});
21438 errdefer msg.destroy(sema.gpa);
21439 try sema.errNote(src, msg, "slice length cannot be inferred from address", .{});
21440 break :msg msg;
21441 };
21442 return sema.failWithOwnedErrorMsg(block, msg);
21443 }
21444
21445 if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| {
21446 if (!is_vector) {
21447 const ptr_val = try sema.ptrFromIntVal(block, operand_src, val, ptr_ty, ptr_align, null);
21448 return Air.internedToRef(ptr_val.toIntern());
21449 }
21450 const len = dest_ty.vectorLen(zcu);
21451 const new_elems = try sema.arena.alloc(InternPool.Index, len);
21452 for (new_elems, 0..) |*new_elem, elem_idx| {
21453 const elem = try val.elemValue(pt, elem_idx);
21454 const ptr_val = try sema.ptrFromIntVal(block, operand_src, elem, ptr_ty, ptr_align, elem_idx);
21455 new_elem.* = ptr_val.toIntern();
21456 }
21457 return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern());
21458 }
21459 try sema.requireRuntimeBlock(block, src, operand_src);
21460 try sema.checkLogicalPtrOperation(block, src, ptr_ty);
21461 if (block.wantSafety()) {
21462 if (!ptr_ty.isAllowzeroPtr(zcu)) {
21463 const is_non_zero = if (is_vector) all_non_zero: {
21464 const zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern());
21465 const is_non_zero = try block.addCmpVector(operand_coerced, zero_usize, .neq);
21466 break :all_non_zero try block.addReduce(is_non_zero, .And);
21467 } else try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize);
21468 try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null);
21469 }
21470 if (ptr_align.compare(.gt, .@"1")) {
21471 const align_bytes_minus_1 = ptr_align.toByteUnits().? - 1;
21472 const align_mask = Air.internedToRef((try sema.splat(operand_ty, try pt.intValue(
21473 .usize,
21474 if (elem_ty.fnPtrMaskOrNull(zcu)) |mask|
21475 align_bytes_minus_1 & mask
21476 else
21477 align_bytes_minus_1,
21478 ))).toIntern());
21479 const remainder = try block.addBinOp(.bit_and, operand_coerced, align_mask);
21480 const is_aligned = if (is_vector) all_aligned: {
21481 const splat_zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern());
21482 const is_aligned = try block.addCmpVector(remainder, splat_zero_usize, .eq);
21483 break :all_aligned try block.addReduce(is_aligned, .And);
21484 } else try block.addBinOp(.cmp_eq, remainder, .zero_usize);
21485 try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment);
21486 }
21487 }
21488 return block.addTyOp(.ptr_from_int, dest_ty, operand_coerced);
21489}
21490
21491fn ptrFromIntVal(
21492 sema: *Sema,
21493 block: *Block,
21494 operand_src: LazySrcLoc,
21495 operand_val: Value,
21496 ptr_ty: Type,
21497 ptr_align: Alignment,
21498 vec_idx: ?usize,
21499) !Value {
21500 const pt = sema.pt;
21501 const zcu = pt.zcu;
21502 if (operand_val.isUndef(zcu)) {
21503 if (ptr_ty.isAllowzeroPtr(zcu) and ptr_align == .@"1") {
21504 return pt.undefValue(ptr_ty);
21505 }
21506 return sema.failWithUseOfUndef(block, operand_src, vec_idx);
21507 }
21508 const addr = operand_val.toUnsignedInt(zcu);
21509 if (!ptr_ty.isAllowzeroPtr(zcu) and addr == 0)
21510 return sema.fail(block, operand_src, "pointer type '{f}' does not allow address zero", .{ptr_ty.fmt(pt)});
21511 if (addr != 0 and ptr_align != .none) {
21512 const masked_addr = if (ptr_ty.childType(zcu).fnPtrMaskOrNull(zcu)) |mask|
21513 addr & mask
21514 else
21515 addr;
21516
21517 if (!ptr_align.check(masked_addr)) {
21518 return sema.fail(block, operand_src, "pointer type '{f}' requires aligned address", .{ptr_ty.fmt(pt)});
21519 }
21520 }
21521
21522 return switch (ptr_ty.zigTypeTag(zcu)) {
21523 .optional => Value.fromInterned(try pt.intern(.{ .opt = .{
21524 .ty = ptr_ty.toIntern(),
21525 .val = if (addr == 0) .none else (try pt.ptrIntValue(ptr_ty.childType(zcu), addr)).toIntern(),
21526 } })),
21527 .pointer => try pt.ptrIntValue(ptr_ty, addr),
21528 else => unreachable,
21529 };
21530}
21531
21532fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
21533 const pt = sema.pt;
21534 const zcu = pt.zcu;
21535 const ip = &zcu.intern_pool;
21536 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
21537 const src = block.nodeOffset(extra.node);
21538 const operand_src = block.builtinCallArgSrc(extra.node, 0);
21539 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast");
21540 const operand = sema.resolveInst(extra.rhs);
21541 const operand_ty = sema.typeOf(operand);
21542
21543 const dest_tag = dest_ty.zigTypeTag(zcu);
21544 const operand_tag = operand_ty.zigTypeTag(zcu);
21545
21546 if (dest_tag != .error_set and dest_tag != .error_union) {
21547 return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)});
21548 }
21549 if (operand_tag != .error_set and operand_tag != .error_union) {
21550 return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(operand_tag)});
21551 }
21552 if (dest_tag == .error_set and operand_tag == .error_union) {
21553 return sema.fail(block, src, "cannot cast an error union type to error set", .{});
21554 }
21555 if (dest_tag == .error_union and operand_tag == .error_union and
21556 dest_ty.errorUnionPayload(zcu).toIntern() != operand_ty.errorUnionPayload(zcu).toIntern())
21557 {
21558 return sema.failWithOwnedErrorMsg(block, msg: {
21559 const msg = try sema.errMsg(src, "payload types of error unions must match", .{});
21560 errdefer msg.destroy(sema.gpa);
21561 const dest_payload_ty = dest_ty.errorUnionPayload(zcu);
21562 const operand_payload_ty = operand_ty.errorUnionPayload(zcu);
21563 try sema.errNote(src, msg, "destination payload is '{f}'", .{dest_payload_ty.fmt(pt)});
21564 try sema.errNote(src, msg, "operand payload is '{f}'", .{operand_payload_ty.fmt(pt)});
21565 try addDeclaredHereNote(sema, msg, dest_ty);
21566 try addDeclaredHereNote(sema, msg, operand_ty);
21567 break :msg msg;
21568 });
21569 }
21570 const dest_err_ty = switch (dest_tag) {
21571 .error_union => dest_ty.errorUnionSet(zcu),
21572 .error_set => dest_ty,
21573 else => unreachable,
21574 };
21575 const operand_err_ty = switch (operand_tag) {
21576 .error_union => operand_ty.errorUnionSet(zcu),
21577 .error_set => operand_ty,
21578 else => unreachable,
21579 };
21580
21581 switch (ip.indexToKey(operand_err_ty.toIntern())) {
21582 .inferred_error_set_type => |func| try sema.ensureFuncIesResolved(block, src, func),
21583 else => {},
21584 }
21585
21586 const result: enum {
21587 /// The operand and destination error sets are disjoint, i.e. have no errors in common.
21588 disjoint,
21589 /// The destination error set is a superset of the operand error set, so the operation is
21590 /// effectively equivalent to a coercion.
21591 superset,
21592 /// The operand and destination error sets have *some* errors in common, but the destination
21593 /// is not a superset of the operand, so a safety check may be needed.
21594 overlap,
21595 } = if (operand_err_ty.errorSetIsEmpty(zcu)) res: {
21596 break :res .disjoint;
21597 } else check: switch (dest_err_ty.toIntern()) {
21598 .anyerror_type => .superset,
21599 .adhoc_inferred_error_set_type => {
21600 // `@errorCast` to this function's own error set.
21601 try sema.fn_ret_ty_ies.?.addErrorSet(operand_err_ty, ip, sema.arena);
21602 break :check .superset;
21603 },
21604 else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) {
21605 .inferred_error_set_type => |func_index| {
21606 if (sema.fn_ret_ty_ies) |dst_ies| {
21607 if (dst_ies.func == func_index) {
21608 // `@errorCast` to this function's own error set.
21609 try sema.fn_ret_ty_ies.?.addErrorSet(operand_err_ty, ip, sema.arena);
21610 break :check .superset;
21611 }
21612 }
21613 try sema.ensureFuncIesResolved(block, src, func_index);
21614 continue :check ip.funcIesResolvedUnordered(func_index);
21615 },
21616 .error_set_type => |dest| {
21617 if (dest.names.len == 0) break :check .disjoint; // dest is 'error{}'
21618 if (operand_err_ty.isAnyError(zcu)) break :check .overlap; // anyerror -> error{...} (non-empty)
21619 var dest_has_all = true;
21620 var dest_has_any = false;
21621 for (operand_err_ty.errorSetNames(zcu).get(ip)) |operand_err_name| {
21622 if (dest.nameIndex(ip, operand_err_name) != null) {
21623 dest_has_any = true;
21624 } else {
21625 dest_has_all = false;
21626 }
21627 }
21628 if (!dest_has_any) break :check .disjoint;
21629 if (dest_has_all) break :check .superset;
21630 break :check .overlap;
21631 },
21632 else => unreachable,
21633 },
21634 };
21635
21636 if (result == .disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) {
21637 return sema.fail(block, src, "error sets '{f}' and '{f}' have no common errors", .{
21638 operand_err_ty.fmt(pt), dest_err_ty.fmt(pt),
21639 });
21640 }
21641
21642 // operand must be defined since it can be an invalid error value
21643 if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
21644 const err_name: InternPool.NullTerminatedString = switch (ip.indexToKey(operand_val.toIntern())) {
21645 .err => |err| err.name,
21646 .error_union => |eu| switch (eu.val) {
21647 .err_name => |name| name,
21648 .payload => |payload_val| {
21649 assert(dest_tag == .error_union); // should be guaranteed from the type checks above
21650 const dest_payload_ty = dest_ty.errorUnionPayload(zcu);
21651 const coerced_payload = try sema.coerce(block, dest_payload_ty, .fromIntern(payload_val), operand_src);
21652 return sema.wrapErrorUnionPayload(block, dest_ty, coerced_payload, operand_src) catch |err| switch (err) {
21653 error.NotCoercible => unreachable,
21654 else => |e| return e,
21655 };
21656 },
21657 },
21658 else => unreachable,
21659 };
21660
21661 if (result != .superset and !dest_err_ty.errorSetHasField(err_name, zcu)) {
21662 return sema.fail(block, src, "'error.{f}' not a member of error set '{f}'", .{
21663 err_name.fmt(ip), dest_err_ty.fmt(pt),
21664 });
21665 }
21666
21667 return .fromIntern(try pt.intern(switch (dest_tag) {
21668 .error_set => .{ .err = .{
21669 .ty = dest_ty.toIntern(),
21670 .name = err_name,
21671 } },
21672 .error_union => .{ .error_union = .{
21673 .ty = dest_ty.toIntern(),
21674 .val = .{ .err_name = err_name },
21675 } },
21676 else => unreachable,
21677 }));
21678 }
21679
21680 const err_int_ty = try pt.errorIntType();
21681 if (block.wantSafety() and result != .superset and zcu.backendSupportsFeature(.error_set_has_value)) {
21682 const err_code_inst = switch (operand_tag) {
21683 .error_set => operand,
21684 .error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand),
21685 else => unreachable,
21686 };
21687 const err_int_inst = try block.addTyOp(.int_from_error, err_int_ty, err_code_inst);
21688 if (dest_tag == .error_union) {
21689 const zero_err = try pt.intRef(err_int_ty, 0);
21690 const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err);
21691 if (result == .disjoint) {
21692 // Error must be zero.
21693 try sema.addSafetyCheckCall(block, src, is_zero, .@"panic.unexpectedErrorCode", &.{err_code_inst});
21694 } else {
21695 // Error must be in destination set or zero.
21696 const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
21697 const ok = try block.addBinOp(.bit_or, has_value, is_zero);
21698 try sema.addSafetyCheckCall(block, src, ok, .@"panic.unexpectedErrorCode", &.{err_code_inst});
21699 }
21700 } else {
21701 const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst);
21702 try sema.addSafetyCheckCall(block, src, ok, .@"panic.unexpectedErrorCode", &.{err_code_inst});
21703 }
21704 }
21705
21706 if (operand_tag == .error_set and dest_tag == .error_union) {
21707 const err_val = try block.addTyOp(.error_cast, dest_err_ty, operand);
21708 return block.addTyOp(.wrap_errunion_err, dest_ty, err_val);
21709 } else {
21710 return block.addTyOp(.error_cast, dest_ty, operand);
21711 }
21712}
21713
21714fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
21715 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
21716 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small)));
21717 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
21718 const src = block.nodeOffset(extra.node);
21719 const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node });
21720 const operand = sema.resolveInst(extra.rhs);
21721 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName());
21722 return sema.ptrCastFull(
21723 block,
21724 flags,
21725 src,
21726 operand,
21727 operand_src,
21728 dest_ty,
21729 flags.needResultTypeBuiltinName(),
21730 );
21731}
21732
21733fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
21734 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
21735 const src = block.nodeOffset(inst_data.src_node);
21736 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
21737 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
21738 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrCast");
21739 const operand = sema.resolveInst(extra.rhs);
21740
21741 return sema.ptrCastFull(
21742 block,
21743 .{ .ptr_cast = true },
21744 src,
21745 operand,
21746 operand_src,
21747 dest_ty,
21748 "@ptrCast",
21749 );
21750}
21751
21752fn ptrCastFull(
21753 sema: *Sema,
21754 block: *Block,
21755 flags: Zir.Inst.FullPtrCastFlags,
21756 src: LazySrcLoc,
21757 operand: Air.Inst.Ref,
21758 operand_src: LazySrcLoc,
21759 dest_ty: Type,
21760 operation: []const u8,
21761) CompileError!Air.Inst.Ref {
21762 const pt = sema.pt;
21763 const zcu = pt.zcu;
21764 const comp = zcu.comp;
21765 const gpa = comp.gpa;
21766 const io = comp.io;
21767
21768 const operand_ty = sema.typeOf(operand);
21769
21770 try sema.checkPtrType(block, src, dest_ty, true);
21771 try sema.checkPtrOperand(block, operand_src, operand_ty);
21772
21773 const src_info = operand_ty.ptrInfo(zcu);
21774 const dest_info = dest_ty.ptrInfo(zcu);
21775
21776 try sema.ensureLayoutResolved(.fromInterned(src_info.child), operand_src, .align_check);
21777 try sema.ensureLayoutResolved(.fromInterned(dest_info.child), src, .align_check);
21778
21779 const DestSliceLen = union(enum) {
21780 undef,
21781 constant: u64,
21782 equal_runtime_src_slice,
21783 change_runtime_src_slice: struct {
21784 bytes_per_src: u64,
21785 bytes_per_dest: u64,
21786 },
21787 };
21788 // Populated iff the destination type is a slice.
21789 const dest_slice_len: ?DestSliceLen = len: {
21790 switch (dest_info.flags.size) {
21791 .slice => {},
21792 .many, .c, .one => break :len null,
21793 }
21794 // A `null` length means the operand is a runtime-known slice (so the length is runtime-known).
21795 // `src_elem_type` is different from `src_info.child` if the latter is an array, to ensure we ignore sentinels.
21796 const src_elem_ty: Type, const opt_src_len: ?u64 = switch (src_info.flags.size) {
21797 .one => src: {
21798 const true_child: Type = .fromInterned(src_info.child);
21799 break :src switch (true_child.zigTypeTag(zcu)) {
21800 .array => .{ true_child.childType(zcu), true_child.arrayLen(zcu) },
21801 else => .{ true_child, 1 },
21802 };
21803 },
21804 .slice => src: {
21805 const operand_val = sema.resolveValue(operand) orelse break :src .{ .fromInterned(src_info.child), null };
21806 if (operand_val.isUndef(zcu)) break :len .undef;
21807 const slice_val = switch (operand_ty.zigTypeTag(zcu)) {
21808 .optional => operand_val.optionalValue(zcu) orelse break :len .undef,
21809 .pointer => operand_val,
21810 else => unreachable,
21811 };
21812 const slice_len: Value = .fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern()));
21813 if (slice_len.isUndef(zcu)) break :len .undef;
21814 break :src .{ .fromInterned(src_info.child), slice_len.toUnsignedInt(zcu) };
21815 },
21816 .many, .c => {
21817 return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)});
21818 },
21819 };
21820 const dest_elem_ty: Type = .fromInterned(dest_info.child);
21821 if (dest_elem_ty.toIntern() == src_elem_ty.toIntern()) {
21822 break :len if (opt_src_len) |l| .{ .constant = l } else .equal_runtime_src_slice;
21823 }
21824 if (!src_elem_ty.comptimeOnly(zcu) and !dest_elem_ty.comptimeOnly(zcu)) {
21825 if (src_elem_ty.zigTypeTag(zcu) == .@"opaque") {
21826 return sema.failWithOwnedErrorMsg(block, msg: {
21827 const msg = try sema.errMsg(src, "cannot infer length of slice of '{f}' from pointer to opaque type '{f}' with unknown size", .{
21828 dest_elem_ty.fmt(pt), src_elem_ty.fmt(pt),
21829 });
21830 errdefer msg.destroy(gpa);
21831 try sema.addDeclaredHereNote(msg, src_elem_ty);
21832 break :msg msg;
21833 });
21834 }
21835 const src_elem_size = src_elem_ty.abiSize(zcu);
21836 const dest_elem_size = dest_elem_ty.abiSize(zcu);
21837 if (dest_elem_size == 0) {
21838 return sema.fail(block, src, "cannot infer length of slice of zero-bit '{f}' from '{f}'", .{
21839 dest_elem_ty.fmt(pt), operand_ty.fmt(pt),
21840 });
21841 }
21842 if (opt_src_len) |src_len| {
21843 const bytes = src_len * src_elem_size;
21844 const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch switch (src_info.flags.size) {
21845 .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}),
21846 .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{Type.fromInterned(src_info.child).fmt(pt)}),
21847 else => unreachable,
21848 };
21849 break :len .{ .constant = dest_len };
21850 }
21851 assert(src_info.flags.size == .slice);
21852 break :len .{ .change_runtime_src_slice = .{
21853 .bytes_per_src = src_elem_size,
21854 .bytes_per_dest = dest_elem_size,
21855 } };
21856 }
21857 // We apply rules for comptime memory consistent with comptime loads/stores, where arrays of
21858 // comptime-only types can be "restructured".
21859 const dest_base_ty: Type, const dest_base_per_elem: u64 = dest_elem_ty.arrayBase(zcu);
21860 const src_base_ty: Type, const src_base_per_elem: u64 = src_elem_ty.arrayBase(zcu);
21861 // The source value has `src_len * src_base_per_elem` values of type `src_base_ty`.
21862 // The result value will have `dest_len * dest_base_per_elem` values of type `dest_base_ty`.
21863 if (dest_base_ty.toIntern() != src_base_ty.toIntern()) {
21864 return sema.fail(block, src, "cannot infer length of comptime-only '{f}' from incompatible '{f}'", .{
21865 dest_ty.fmt(pt), operand_ty.fmt(pt),
21866 });
21867 }
21868 // `src_base_ty` is comptime-only, so `src_elem_ty` is comptime-only, so `operand_ty` is
21869 // comptime-only, so `operand` is comptime-known, so `opt_src_len` is non-`null`.
21870 const src_len = opt_src_len.?;
21871 const base_len = src_len * src_base_per_elem;
21872 const dest_len = std.math.divExact(u64, base_len, dest_base_per_elem) catch switch (src_info.flags.size) {
21873 .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}),
21874 .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{src_elem_ty.fmt(pt)}),
21875 else => unreachable,
21876 };
21877 break :len .{ .constant = dest_len };
21878 };
21879
21880 // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs
21881
21882 if (!flags.ptr_cast) {
21883 const is_array_ptr_to_slice = b: {
21884 if (dest_info.flags.size != .slice) break :b false;
21885 if (src_info.flags.size != .one) break :b false;
21886 const src_pointer_child: Type = .fromInterned(src_info.child);
21887 if (src_pointer_child.zigTypeTag(zcu) != .array) break :b false;
21888 const src_elem = src_pointer_child.childType(zcu);
21889 break :b src_elem.toIntern() == dest_info.child;
21890 };
21891
21892 check_size: {
21893 if (src_info.flags.size == dest_info.flags.size) break :check_size;
21894 if (is_array_ptr_to_slice) break :check_size;
21895 if (src_info.flags.size == .c) break :check_size;
21896 if (dest_info.flags.size == .c) break :check_size;
21897 return sema.failWithOwnedErrorMsg(block, msg: {
21898 const msg = try sema.errMsg(src, "cannot implicitly convert {s} to {s}", .{
21899 pointerSizeString(src_info.flags.size),
21900 pointerSizeString(dest_info.flags.size),
21901 });
21902 errdefer msg.destroy(sema.gpa);
21903 if (dest_info.flags.size == .many and
21904 (src_info.flags.size == .slice or
21905 (src_info.flags.size == .one and Type.fromInterned(src_info.child).zigTypeTag(zcu) == .array)))
21906 {
21907 try sema.errNote(src, msg, "use 'ptr' field to convert slice to many pointer", .{});
21908 } else {
21909 try sema.errNote(src, msg, "use @ptrCast to change pointer size", .{});
21910 }
21911 break :msg msg;
21912 });
21913 }
21914
21915 check_child: {
21916 const src_child: Type = if (dest_info.flags.size == .slice and src_info.flags.size == .one) blk: {
21917 // *[n]T -> []T
21918 break :blk Type.fromInterned(src_info.child).childType(zcu);
21919 } else .fromInterned(src_info.child);
21920
21921 const dest_child: Type = .fromInterned(dest_info.child);
21922
21923 const imc_res = try sema.coerceInMemoryAllowed(
21924 block,
21925 dest_child,
21926 src_child,
21927 !dest_info.flags.is_const,
21928 zcu.getTarget(),
21929 src,
21930 operand_src,
21931 null,
21932 );
21933 if (imc_res == .ok) break :check_child;
21934 return sema.failWithOwnedErrorMsg(block, msg: {
21935 const msg = try sema.errMsg(src, "pointer element type '{f}' cannot coerce into element type '{f}'", .{
21936 src_child.fmt(pt), dest_child.fmt(pt),
21937 });
21938 errdefer msg.destroy(sema.gpa);
21939 try imc_res.report(sema, src, msg);
21940 try sema.errNote(src, msg, "use @ptrCast to cast pointer element type", .{});
21941 break :msg msg;
21942 });
21943 }
21944
21945 check_sent: {
21946 if (dest_info.sentinel == .none) break :check_sent;
21947 if (src_info.flags.size == .c) break :check_sent;
21948 if (src_info.sentinel != .none) {
21949 const coerced_sent = try zcu.intern_pool.getCoerced(gpa, io, pt.tid, src_info.sentinel, dest_info.child);
21950 if (dest_info.sentinel == coerced_sent) break :check_sent;
21951 }
21952 if (is_array_ptr_to_slice) {
21953 // [*]nT -> []T
21954 const arr_ty: Type = .fromInterned(src_info.child);
21955 if (arr_ty.sentinel(zcu)) |src_sentinel| {
21956 const coerced_sent = try zcu.intern_pool.getCoerced(gpa, io, pt.tid, src_sentinel.toIntern(), dest_info.child);
21957 if (dest_info.sentinel == coerced_sent) break :check_sent;
21958 }
21959 }
21960 return sema.failWithOwnedErrorMsg(block, msg: {
21961 const msg = if (src_info.sentinel == .none) blk: {
21962 break :blk try sema.errMsg(src, "destination pointer requires '{f}' sentinel", .{
21963 Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema),
21964 });
21965 } else blk: {
21966 break :blk try sema.errMsg(src, "pointer sentinel '{f}' cannot coerce into pointer sentinel '{f}'", .{
21967 Value.fromInterned(src_info.sentinel).fmtValueSema(pt, sema),
21968 Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema),
21969 });
21970 };
21971 errdefer msg.destroy(sema.gpa);
21972 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{});
21973 break :msg msg;
21974 });
21975 }
21976
21977 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) {
21978 return sema.failWithOwnedErrorMsg(block, msg: {
21979 const msg = try sema.errMsg(src, "pointer host size '{d}' cannot coerce into pointer host size '{d}'", .{
21980 src_info.packed_offset.host_size,
21981 dest_info.packed_offset.host_size,
21982 });
21983 errdefer msg.destroy(sema.gpa);
21984 try sema.errNote(src, msg, "use @ptrCast to cast pointer host size", .{});
21985 break :msg msg;
21986 });
21987 }
21988
21989 if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) {
21990 return sema.failWithOwnedErrorMsg(block, msg: {
21991 const msg = try sema.errMsg(src, "pointer bit offset '{d}' cannot coerce into pointer bit offset '{d}'", .{
21992 src_info.packed_offset.bit_offset,
21993 dest_info.packed_offset.bit_offset,
21994 });
21995 errdefer msg.destroy(sema.gpa);
21996 try sema.errNote(src, msg, "use @ptrCast to cast pointer bit offset", .{});
21997 break :msg msg;
21998 });
21999 }
22000
22001 check_allowzero: {
22002 const src_allows_zero = operand_ty.ptrAllowsZero(zcu);
22003 const dest_allows_zero = dest_ty.ptrAllowsZero(zcu);
22004 if (!src_allows_zero) break :check_allowzero;
22005 if (dest_allows_zero) break :check_allowzero;
22006
22007 return sema.failWithOwnedErrorMsg(block, msg: {
22008 const msg = try sema.errMsg(src, "'{f}' could have null values which are illegal in type '{f}'", .{
22009 operand_ty.fmt(pt),
22010 dest_ty.fmt(pt),
22011 });
22012 errdefer msg.destroy(sema.gpa);
22013 try sema.errNote(src, msg, "use @ptrCast to assert the pointer is not null", .{});
22014 break :msg msg;
22015 });
22016 }
22017
22018 // TODO: vector index?
22019 }
22020
22021 const src_align = if (src_info.flags.alignment != .none)
22022 src_info.flags.alignment
22023 else
22024 Type.fromInterned(src_info.child).abiAlignment(zcu);
22025
22026 const dest_align = if (dest_info.flags.alignment != .none)
22027 dest_info.flags.alignment
22028 else
22029 Type.fromInterned(dest_info.child).abiAlignment(zcu);
22030
22031 if (!flags.align_cast) {
22032 if (dest_align.compare(.gt, src_align)) {
22033 return sema.failWithOwnedErrorMsg(block, msg: {
22034 const msg = try sema.errMsg(src, "{s} increases pointer alignment", .{operation});
22035 errdefer msg.destroy(sema.gpa);
22036 try sema.errNote(operand_src, msg, "'{f}' has alignment '{d}'", .{
22037 operand_ty.fmt(pt), src_align.toByteUnits() orelse 0,
22038 });
22039 try sema.errNote(src, msg, "'{f}' has alignment '{d}'", .{
22040 dest_ty.fmt(pt), dest_align.toByteUnits() orelse 0,
22041 });
22042 try sema.errNote(src, msg, "use @alignCast to assert pointer alignment", .{});
22043 break :msg msg;
22044 });
22045 }
22046 }
22047
22048 if (!flags.addrspace_cast) {
22049 if (src_info.flags.address_space != dest_info.flags.address_space) {
22050 return sema.failWithOwnedErrorMsg(block, msg: {
22051 const msg = try sema.errMsg(src, "{s} changes pointer address space", .{operation});
22052 errdefer msg.destroy(sema.gpa);
22053 try sema.errNote(operand_src, msg, "'{f}' has address space '{s}'", .{
22054 operand_ty.fmt(pt), @tagName(src_info.flags.address_space),
22055 });
22056 try sema.errNote(src, msg, "'{f}' has address space '{s}'", .{
22057 dest_ty.fmt(pt), @tagName(dest_info.flags.address_space),
22058 });
22059 try sema.errNote(src, msg, "use @addrSpaceCast to cast pointer address space", .{});
22060 break :msg msg;
22061 });
22062 }
22063 } else {
22064 // Some address space casts are always disallowed
22065 if (!target_util.addrSpaceCastIsValid(zcu.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) {
22066 return sema.failWithOwnedErrorMsg(block, msg: {
22067 const msg = try sema.errMsg(src, "invalid address space cast", .{});
22068 errdefer msg.destroy(sema.gpa);
22069 try sema.errNote(operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{
22070 @tagName(src_info.flags.address_space),
22071 @tagName(dest_info.flags.address_space),
22072 });
22073 break :msg msg;
22074 });
22075 }
22076 }
22077
22078 if (!flags.const_cast) {
22079 if (src_info.flags.is_const and !dest_info.flags.is_const) {
22080 return sema.failWithOwnedErrorMsg(block, msg: {
22081 const msg = try sema.errMsg(src, "{s} discards const qualifier", .{operation});
22082 errdefer msg.destroy(sema.gpa);
22083 try sema.errNote(src, msg, "use @constCast to discard const qualifier", .{});
22084 break :msg msg;
22085 });
22086 }
22087 }
22088
22089 if (!flags.volatile_cast) {
22090 if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) {
22091 return sema.failWithOwnedErrorMsg(block, msg: {
22092 const msg = try sema.errMsg(src, "{s} discards volatile qualifier", .{operation});
22093 errdefer msg.destroy(sema.gpa);
22094 try sema.errNote(src, msg, "use @volatileCast to discard volatile qualifier", .{});
22095 break :msg msg;
22096 });
22097 }
22098 }
22099
22100 // Type validation done -- this cast is okay. Let's do it!
22101 //
22102 // `operand` is a maybe-optional pointer or slice.
22103 // `dest_ty` is a maybe-optional pointer or slice.
22104 //
22105 // We have a few safety checks:
22106 // * if the destination does not allow zero, check the operand is not null / 0
22107 // * if the destination is more aligned than the operand, check the pointer alignment
22108 // * if `slice_needs_len_change`, check the element count divides neatly
22109
22110 ct: {
22111 if (flags.addrspace_cast) break :ct; // cannot `@addrSpaceCast` at comptime
22112 const operand_val = sema.resolveValue(operand) orelse break :ct;
22113
22114 if (operand_val.isUndef(zcu)) {
22115 if (!dest_ty.ptrAllowsZero(zcu)) {
22116 return sema.failWithUseOfUndef(block, operand_src, null);
22117 }
22118 return pt.undefRef(dest_ty);
22119 }
22120
22121 if (operand_val.isNull(zcu)) {
22122 if (!dest_ty.ptrAllowsZero(zcu)) {
22123 return sema.fail(block, operand_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)});
22124 }
22125 if (dest_ty.zigTypeTag(zcu) == .optional) {
22126 return Air.internedToRef((try pt.nullValue(dest_ty)).toIntern());
22127 } else {
22128 return Air.internedToRef((try pt.ptrIntValue(dest_ty, 0)).toIntern());
22129 }
22130 }
22131
22132 const ptr_val: Value = switch (src_info.flags.size) {
22133 .slice => .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.ptr),
22134 .one, .many, .c => operand_val,
22135 };
22136
22137 if (dest_align.compare(.gt, src_align)) {
22138 if (ptr_val.getUnsignedInt(zcu)) |addr| {
22139 const masked_addr = if (Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu)) |mask|
22140 addr & mask
22141 else
22142 addr;
22143
22144 if (!dest_align.check(masked_addr)) {
22145 return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{
22146 addr,
22147 dest_align.toByteUnits().?,
22148 });
22149 }
22150 }
22151 }
22152
22153 if (dest_info.flags.size == .slice) {
22154 // Because the operand is comptime-known and not `null`, the slice length has already been computed:
22155 const len: Value = switch (dest_slice_len.?) {
22156 .undef => .undef_usize,
22157 .constant => |n| try pt.intValue(.usize, n),
22158 .equal_runtime_src_slice => unreachable,
22159 .change_runtime_src_slice => unreachable,
22160 };
22161 const dest_is_optional = dest_ty.zigTypeTag(zcu) == .optional;
22162 const slice_ty = if (dest_is_optional) dest_ty.optionalChild(zcu) else dest_ty;
22163 const slice_val = try pt.intern(.{ .slice = .{
22164 .ty = slice_ty.toIntern(),
22165 .ptr = (try pt.getCoerced(ptr_val, slice_ty.slicePtrFieldType(zcu))).toIntern(),
22166 .len = len.toIntern(),
22167 } });
22168 if (!dest_is_optional) return Air.internedToRef(slice_val);
22169 return Air.internedToRef(try pt.intern(.{ .opt = .{
22170 .ty = dest_ty.toIntern(),
22171 .val = slice_val,
22172 } }));
22173 } else {
22174 // Any to non-slice
22175 const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty);
22176 return Air.internedToRef(new_ptr_val.toIntern());
22177 }
22178 }
22179
22180 try sema.validateRuntimeValue(block, operand_src, operand);
22181 try sema.checkLogicalPtrCast(block, src, operand_ty, dest_ty);
22182
22183 const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu));
22184 const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu);
22185 const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align);
22186
22187 const slice_needs_len_change = if (dest_slice_len) |l| switch (l) {
22188 .undef, .equal_runtime_src_slice => false,
22189 .constant, .change_runtime_src_slice => true,
22190 } else false;
22191
22192 // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer.
22193 const need_operand_ptr = src_info.flags.size != .slice or // we already have it
22194 dest_info.flags.size != .slice or // the result is a raw pointer
22195 need_null_check or // safety check happens on pointer
22196 need_align_check or // safety check happens on pointer
22197 flags.addrspace_cast or // AIR addrspace_cast acts on a pointer
22198 slice_needs_len_change; // to change the length, we reconstruct the slice
22199
22200 // This is not quite just the pointer part of `operand` -- it's also had the address space cast done already.
22201 const operand_ptr: Air.Inst.Ref = ptr: {
22202 if (!need_operand_ptr) break :ptr .none;
22203 // First, just get the pointer.
22204 const pre_addrspace_cast = inner: {
22205 if (src_info.flags.size != .slice) break :inner operand;
22206 if (operand_ty.zigTypeTag(zcu) == .optional) {
22207 break :inner try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty);
22208 } else {
22209 break :inner try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty);
22210 }
22211 };
22212 // Now, do an addrspace cast if necessary!
22213 if (!flags.addrspace_cast) break :ptr pre_addrspace_cast;
22214
22215 const intermediate_ptr_ty = try pt.ptrType(info: {
22216 var info = src_info;
22217 info.flags.address_space = dest_info.flags.address_space;
22218 break :info info;
22219 });
22220 const intermediate_ty = if (operand_ty.zigTypeTag(zcu) == .optional) blk: {
22221 break :blk try pt.optionalType(intermediate_ptr_ty.toIntern());
22222 } else intermediate_ptr_ty;
22223 break :ptr try block.addInst(.{
22224 .tag = .addrspace_cast,
22225 .data = .{ .ty_op = .{
22226 .ty = intermediate_ty,
22227 .operand = pre_addrspace_cast,
22228 } },
22229 });
22230 };
22231
22232 // Whether we need to know if the (slice) operand has `len == 0`.
22233 const need_operand_len_is_zero = src_info.flags.size == .slice and
22234 dest_info.flags.size == .slice and
22235 (need_null_check or need_align_check);
22236 // Whether we need to get the (slice) operand's `len`.
22237 const need_operand_len = need_len: {
22238 if (src_info.flags.size != .slice) break :need_len false;
22239 if (dest_info.flags.size != .slice) break :need_len false;
22240 if (need_operand_len_is_zero) break :need_len true;
22241 if (flags.addrspace_cast or slice_needs_len_change) break :need_len true;
22242 break :need_len false;
22243 };
22244 // `.none` if `!need_operand_len`.
22245 const operand_len: Air.Inst.Ref = len: {
22246 if (!need_operand_len) break :len .none;
22247 break :len try block.addTyOp(.slice_len, .usize, operand);
22248 };
22249 // `.none` if `!need_operand_len_is_zero`.
22250 const operand_len_is_zero: Air.Inst.Ref = zero: {
22251 if (!need_operand_len_is_zero) break :zero .none;
22252 assert(need_operand_len);
22253 break :zero try block.addBinOp(.cmp_eq, operand_len, .zero_usize);
22254 };
22255
22256 // `operand_ptr` converted to an integer, for safety checks.
22257 const operand_ptr_int: Air.Inst.Ref = if (need_null_check or need_align_check) i: {
22258 assert(need_operand_ptr);
22259 break :i try block.addTyOp(.int_from_ptr, .usize, operand_ptr);
22260 } else .none;
22261
22262 if (need_null_check) {
22263 assert(operand_ptr_int != .none);
22264 const ptr_is_non_zero = try block.addBinOp(.cmp_neq, operand_ptr_int, .zero_usize);
22265 const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: {
22266 break :ok try block.addBinOp(.bit_or, operand_len_is_zero, ptr_is_non_zero);
22267 } else ptr_is_non_zero;
22268 try sema.addSafetyCheck(block, src, ok, .cast_to_null);
22269 }
22270 if (need_align_check) {
22271 assert(operand_ptr_int != .none);
22272 const align_mask = try pt.intRef(.usize, mask: {
22273 const target_ptr_mask = Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu) orelse ~@as(u64, 0);
22274 break :mask (dest_align.toByteUnits().? - 1) & target_ptr_mask;
22275 });
22276 const ptr_masked = try block.addBinOp(.bit_and, operand_ptr_int, align_mask);
22277 const is_aligned = try block.addBinOp(.cmp_eq, ptr_masked, .zero_usize);
22278 const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: {
22279 break :ok try block.addBinOp(.bit_or, operand_len_is_zero, is_aligned);
22280 } else is_aligned;
22281 try sema.addSafetyCheck(block, src, ok, .incorrect_alignment);
22282 }
22283
22284 if (dest_info.flags.size == .slice) {
22285 if (src_info.flags.size == .slice and !flags.addrspace_cast and !slice_needs_len_change) {
22286 // Fast path: just pointer cast!
22287 return block.addTyOp(.ptr_cast, dest_ty, operand);
22288 }
22289
22290 // We need to deconstruct the slice (if applicable) and reconstruct it.
22291 assert(need_operand_ptr);
22292
22293 const result_len: Air.Inst.Ref = switch (dest_slice_len.?) {
22294 .undef => .undef_usize,
22295 .constant => |n| try pt.intRef(.usize, n),
22296 .equal_runtime_src_slice => len: {
22297 assert(need_operand_len);
22298 break :len operand_len;
22299 },
22300 .change_runtime_src_slice => |change| len: {
22301 assert(need_operand_len);
22302 // If `mul / div` is a whole number, then just multiply the length by it.
22303 if (std.math.divExact(u64, change.bytes_per_src, change.bytes_per_dest)) |dest_per_src| {
22304 const multiplier = try pt.intRef(.usize, dest_per_src);
22305 break :len try block.addBinOp(.mul, operand_len, multiplier);
22306 } else |err| switch (err) {
22307 error.DivisionByZero => unreachable,
22308 error.UnexpectedRemainder => {}, // fall through to code below
22309 }
22310 // If `div / mul` is a whole number, then just divide the length by it.
22311 // This incurs a safety check.
22312 if (std.math.divExact(u64, change.bytes_per_dest, change.bytes_per_src)) |src_per_dest| {
22313 const divisor = try pt.intRef(.usize, src_per_dest);
22314 if (block.wantSafety()) {
22315 // Check that the element count divides neatly.
22316 const remainder = try block.addBinOp(.rem, operand_len, divisor);
22317 const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
22318 try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len});
22319 }
22320 break :len try block.addBinOp(.div_exact, operand_len, divisor);
22321 } else |err| switch (err) {
22322 error.DivisionByZero => unreachable,
22323 error.UnexpectedRemainder => {}, // fall through to code below
22324 }
22325 // Fallback: the elements don't divide easily. We'll multiply *and* divide. This incurs a safety check.
22326 const total_bytes_ref = try block.addBinOp(.mul, operand_len, try pt.intRef(.usize, change.bytes_per_src));
22327 const bytes_per_dest_ref = try pt.intRef(.usize, change.bytes_per_dest);
22328 if (block.wantSafety()) {
22329 // Check that `total_bytes_ref` divides neatly into `bytes_per_dest_ref`.
22330 const remainder = try block.addBinOp(.rem, total_bytes_ref, bytes_per_dest_ref);
22331 const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize);
22332 try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len});
22333 }
22334 break :len try block.addBinOp(.div_exact, total_bytes_ref, bytes_per_dest_ref);
22335 },
22336 };
22337
22338 const operand_ptr_ty = sema.typeOf(operand_ptr);
22339 const want_ptr_ty = switch (dest_ty.zigTypeTag(zcu)) {
22340 .optional => try pt.optionalType(dest_ty.childType(zcu).slicePtrFieldType(zcu).toIntern()),
22341 .pointer => dest_ty.slicePtrFieldType(zcu),
22342 else => unreachable,
22343 };
22344 const coerced_ptr = if (operand_ptr_ty.toIntern() != want_ptr_ty.toIntern()) ptr: {
22345 break :ptr try block.addTyOp(.ptr_cast, want_ptr_ty, operand_ptr);
22346 } else operand_ptr;
22347
22348 return block.addInst(.{
22349 .tag = .slice,
22350 .data = .{ .ty_pl = .{
22351 .ty = dest_ty,
22352 .payload = try sema.addExtra(Air.Bin{
22353 .lhs = coerced_ptr,
22354 .rhs = result_len,
22355 }),
22356 } },
22357 });
22358 } else {
22359 assert(need_operand_ptr);
22360 // We just need a ptr_cast, if even that (we might only have needed the `addrspace_cast`).
22361 const result = if (sema.typeOf(operand_ptr).toIntern() == dest_ty.toIntern())
22362 operand_ptr
22363 else
22364 try block.addTyOp(.ptr_cast, dest_ty, operand_ptr);
22365
22366 try sema.checkKnownAllocPtr(block, operand, result);
22367 return result;
22368 }
22369}
22370
22371fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
22372 const pt = sema.pt;
22373 const zcu = pt.zcu;
22374 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
22375 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small)));
22376 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
22377 const src = block.nodeOffset(extra.node);
22378 const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node });
22379 const operand = sema.resolveInst(extra.operand);
22380 const operand_ty = sema.typeOf(operand);
22381 try sema.checkPtrOperand(block, operand_src, operand_ty);
22382
22383 var ptr_info = operand_ty.ptrInfo(zcu);
22384 if (flags.const_cast) ptr_info.flags.is_const = false;
22385 if (flags.volatile_cast) ptr_info.flags.is_volatile = false;
22386
22387 const dest_ty = blk: {
22388 const dest_ty = try pt.ptrType(ptr_info);
22389 if (operand_ty.zigTypeTag(zcu) == .optional) {
22390 break :blk try pt.optionalType(dest_ty.toIntern());
22391 }
22392 break :blk dest_ty;
22393 };
22394
22395 if (sema.resolveValue(operand)) |operand_val| {
22396 return Air.internedToRef((try pt.getCoerced(operand_val, dest_ty)).toIntern());
22397 }
22398
22399 try sema.requireRuntimeBlock(block, src, null);
22400 const new_ptr = try block.addTyOp(.ptr_cast, dest_ty, operand);
22401 try sema.checkKnownAllocPtr(block, operand, new_ptr);
22402 return new_ptr;
22403}
22404
22405fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
22406 const pt = sema.pt;
22407 const zcu = pt.zcu;
22408 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
22409 const src = block.nodeOffset(inst_data.src_node);
22410 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
22411 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
22412 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate");
22413 const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src);
22414 const operand = sema.resolveInst(extra.rhs);
22415 const operand_ty = sema.typeOf(operand);
22416 const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src);
22417
22418 const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector;
22419 const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector;
22420 if (operand_is_vector != dest_is_vector) {
22421 return sema.failWithTypeMismatch(block, operand_src, dest_ty, operand_ty);
22422 }
22423
22424 if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) {
22425 return sema.coerce(block, dest_ty, operand, operand_src);
22426 }
22427
22428 if (try dest_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
22429
22430 const dest_info = dest_scalar_ty.intInfo(zcu);
22431
22432 if (operand_scalar_ty.zigTypeTag(zcu) != .comptime_int) {
22433 const operand_info = operand_ty.intInfo(zcu);
22434
22435 if (operand_info.signedness != dest_info.signedness) {
22436 return sema.fail(block, operand_src, "expected {s} integer type, found '{f}'", .{
22437 @tagName(dest_info.signedness), operand_ty.fmt(pt),
22438 });
22439 }
22440 if (dest_info.bits >= operand_info.bits) {
22441 return sema.coerce(block, dest_ty, operand, operand_src);
22442 }
22443 }
22444
22445 if (sema.resolveValue(operand)) |val| {
22446 const result_val = try arith.truncate(sema, val, operand_ty, dest_ty, dest_info.signedness, dest_info.bits);
22447 return Air.internedToRef(result_val.toIntern());
22448 }
22449
22450 try sema.requireRuntimeBlock(block, src, operand_src);
22451 return block.addTyOp(.trunc, dest_ty, operand);
22452}
22453
22454fn zirBitCount(
22455 sema: *Sema,
22456 block: *Block,
22457 inst: Zir.Inst.Index,
22458 air_tag: Air.Inst.Tag,
22459 comptime comptimeOp: fn (val: Value, ty: Type, zcu: *Zcu) u64,
22460) CompileError!Air.Inst.Ref {
22461 const pt = sema.pt;
22462 const zcu = pt.zcu;
22463 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
22464 const src = block.nodeOffset(inst_data.src_node);
22465 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
22466 const operand = sema.resolveInst(inst_data.operand);
22467 const operand_ty = sema.typeOf(operand);
22468 _ = try sema.checkIntOrVector(block, operand, operand_src);
22469 const bits = operand_ty.intInfo(zcu).bits;
22470
22471 const result_scalar_ty = try pt.smallestUnsignedInt(bits);
22472 switch (operand_ty.zigTypeTag(zcu)) {
22473 .vector => {
22474 const vec_len = operand_ty.vectorLen(zcu);
22475 const result_ty = try pt.vectorType(.{
22476 .len = vec_len,
22477 .child = result_scalar_ty.toIntern(),
22478 });
22479 if (sema.resolveValue(operand)) |val| {
22480 if (val.isUndef(zcu)) return pt.undefRef(result_ty);
22481
22482 const elems = try sema.arena.alloc(InternPool.Index, vec_len);
22483 const scalar_ty = operand_ty.scalarType(zcu);
22484 for (elems, 0..) |*elem, i| {
22485 const elem_val = try val.elemValue(pt, i);
22486 const count = comptimeOp(elem_val, scalar_ty, zcu);
22487 elem.* = (try pt.intValue(result_scalar_ty, count)).toIntern();
22488 }
22489 return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern());
22490 } else {
22491 try sema.requireRuntimeBlock(block, src, operand_src);
22492 return block.addTyOp(air_tag, result_ty, operand);
22493 }
22494 },
22495 .int => {
22496 if (sema.resolveValue(operand)) |val| {
22497 if (val.isUndef(zcu)) return pt.undefRef(result_scalar_ty);
22498 return pt.intRef(result_scalar_ty, comptimeOp(val, operand_ty, zcu));
22499 } else {
22500 try sema.requireRuntimeBlock(block, src, operand_src);
22501 return block.addTyOp(air_tag, result_scalar_ty, operand);
22502 }
22503 },
22504 else => unreachable,
22505 }
22506}
22507
22508fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
22509 const pt = sema.pt;
22510 const zcu = pt.zcu;
22511 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
22512 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
22513 const operand = sema.resolveInst(inst_data.operand);
22514 const operand_ty = sema.typeOf(operand);
22515 const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src);
22516 const bits = scalar_ty.intInfo(zcu).bits;
22517 if (bits % 8 != 0) {
22518 return sema.fail(
22519 block,
22520 operand_src,
22521 "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {d} bits",
22522 .{ scalar_ty.fmt(pt), bits },
22523 );
22524 }
22525 if (sema.resolveValue(operand)) |operand_val| {
22526 return .fromValue(try arith.byteSwap(sema, operand_val, operand_ty));
22527 }
22528 return block.addTyOp(.byte_swap, operand_ty, operand);
22529}
22530
22531fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
22532 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
22533 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0);
22534 const operand = sema.resolveInst(inst_data.operand);
22535 const operand_ty = sema.typeOf(operand);
22536 _ = try sema.checkIntOrVector(block, operand, operand_src);
22537
22538 if (sema.resolveValue(operand)) |operand_val| {
22539 return .fromValue(try arith.bitReverse(sema, operand_val, operand_ty));
22540 }
22541 return block.addTyOp(.bit_reverse, operand_ty, operand);
22542}
22543
22544fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
22545 const offset = try sema.bitOffsetOf(block, inst);
22546 return sema.pt.intRef(.comptime_int, offset);
22547}
22548
22549fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
22550 const offset = try sema.bitOffsetOf(block, inst);
22551 // TODO reminder to make this a compile error for packed structs
22552 return sema.pt.intRef(.comptime_int, offset / 8);
22553}
22554
22555fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
22556 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
22557 const src = block.src(.{ .node_offset_bin_op = inst_data.src_node });
22558 const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
22559 const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1);
22560 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
22561
22562 const ty = try sema.resolveType(block, ty_src, extra.lhs);
22563 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name });
22564
22565 try sema.ensureLayoutResolved(ty, ty_src, .field_queried);
22566
22567 const pt = sema.pt;
22568 const zcu = pt.zcu;
22569 const ip = &zcu.intern_pool;
22570 switch (ty.zigTypeTag(zcu)) {
22571 .@"struct" => {},
22572 else => return sema.fail(block, ty_src, "expected struct type, found '{f}'", .{ty.fmt(pt)}),
22573 }
22574
22575 const field_index = if (ty.isTuple(zcu)) blk: {
22576 if (field_name.eqlSlice("len", ip)) {
22577 return sema.fail(block, src, "no offset available for 'len' field of tuple", .{});
22578 }
22579 break :blk try sema.tupleFieldIndex(block, ty, field_name, field_name_src);
22580 } else try sema.structFieldIndex(block, ty, field_name, field_name_src);
22581
22582 if (ty.structFieldIsComptime(field_index, zcu)) {
22583 return sema.fail(block, src, "no offset available for comptime field", .{});
22584 }
22585
22586 switch (ty.containerLayout(zcu)) {
22587 .@"packed" => {
22588 var bit_sum: u64 = 0;
22589 const struct_type = ip.loadStructType(ty.toIntern());
22590 for (0..struct_type.field_types.len) |i| {
22591 if (i == field_index) {
22592 return bit_sum;
22593 }
22594 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
22595 bit_sum += field_ty.bitSize(zcu);
22596 } else unreachable;
22597 },
22598 else => return ty.structFieldOffset(field_index, zcu) * 8,
22599 }
22600}
22601
22602fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void {
22603 const pt = sema.pt;
22604 const zcu = pt.zcu;
22605 switch (ty.zigTypeTag(zcu)) {
22606 .@"struct", .@"enum", .@"union", .@"opaque" => return,
22607 else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{f}'", .{ty.fmt(pt)}),
22608 }
22609}
22610
22611/// Returns `true` if the type was a comptime_int.
22612fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
22613 const pt = sema.pt;
22614 const zcu = pt.zcu;
22615 switch (ty.zigTypeTag(zcu)) {
22616 .comptime_int => return true,
22617 .int => return false,
22618 else => return sema.fail(block, src, "expected integer type, found '{f}'", .{ty.fmt(pt)}),
22619 }
22620}
22621
22622fn checkInvalidPtrIntArithmetic(
22623 sema: *Sema,
22624 block: *Block,
22625 src: LazySrcLoc,
22626 ty: Type,
22627) CompileError!void {
22628 const pt = sema.pt;
22629 const zcu = pt.zcu;
22630 switch (ty.zigTypeTag(zcu)) {
22631 .pointer => switch (ty.ptrSize(zcu)) {
22632 .one, .slice => return,
22633 .many, .c => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"),
22634 },
22635 else => return,
22636 }
22637}
22638
22639fn checkArithmeticOp(
22640 sema: *Sema,
22641 block: *Block,
22642 src: LazySrcLoc,
22643 scalar_tag: std.lang.TypeId,
22644 lhs_zig_ty_tag: std.lang.TypeId,
22645 rhs_zig_ty_tag: std.lang.TypeId,
22646 zir_tag: Zir.Inst.Tag,
22647) CompileError!void {
22648 const is_int = scalar_tag == .int or scalar_tag == .comptime_int;
22649 const is_float = scalar_tag == .float or scalar_tag == .comptime_float;
22650
22651 if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
22652 return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
22653 @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
22654 });
22655 }
22656}
22657
22658fn checkPtrOperand(
22659 sema: *Sema,
22660 block: *Block,
22661 ty_src: LazySrcLoc,
22662 ty: Type,
22663) CompileError!void {
22664 const pt = sema.pt;
22665 const zcu = pt.zcu;
22666 switch (ty.zigTypeTag(zcu)) {
22667 .pointer => return,
22668 .@"fn" => {
22669 const msg = msg: {
22670 const msg = try sema.errMsg(
22671 ty_src,
22672 "expected pointer, found '{f}'",
22673 .{ty.fmt(pt)},
22674 );
22675 errdefer msg.destroy(sema.gpa);
22676
22677 try sema.errNote(ty_src, msg, "use '&' to obtain a function pointer", .{});
22678
22679 break :msg msg;
22680 };
22681 return sema.failWithOwnedErrorMsg(block, msg);
22682 },
22683 .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return,
22684 else => {},
22685 }
22686 return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)});
22687}
22688
22689fn checkPtrType(
22690 sema: *Sema,
22691 block: *Block,
22692 ty_src: LazySrcLoc,
22693 ty: Type,
22694 allow_slice: bool,
22695) CompileError!void {
22696 const pt = sema.pt;
22697 const zcu = pt.zcu;
22698 switch (ty.zigTypeTag(zcu)) {
22699 .pointer => if (allow_slice or !ty.isSlice(zcu)) return,
22700 .@"fn" => {
22701 const msg = msg: {
22702 const msg = try sema.errMsg(
22703 ty_src,
22704 "expected pointer type, found '{f}'",
22705 .{ty.fmt(pt)},
22706 );
22707 errdefer msg.destroy(sema.gpa);
22708
22709 try sema.errNote(ty_src, msg, "use '*const ' to make a function pointer type", .{});
22710
22711 break :msg msg;
22712 };
22713 return sema.failWithOwnedErrorMsg(block, msg);
22714 },
22715 .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return,
22716 else => {},
22717 }
22718 return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)});
22719}
22720
22721fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
22722 const pt = sema.pt;
22723 const zcu = pt.zcu;
22724
22725 if (block.isComptime() or block.is_typeof) return;
22726 if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) {
22727 const target = zcu.getTarget();
22728 const as = ty.ptrAddressSpace(zcu);
22729 if (target_util.shouldBlockPointerOps(target, as)) {
22730 return sema.failWithOwnedErrorMsg(block, msg: {
22731 const msg = try sema.errMsg(src, "illegal operation on logical pointer of type '{f}'", .{ty.fmt(pt)});
22732 errdefer msg.destroy(sema.gpa);
22733 try sema.errNote(
22734 src,
22735 msg,
22736 "pointers with address space '{t}' do not support arithmetic or indexing on target {t}-{t}",
22737 .{ as, target.cpu.arch.family(), target.os.tag },
22738 );
22739 break :msg msg;
22740 });
22741 }
22742 }
22743}
22744
22745fn checkLogicalPtrCast(
22746 sema: *Sema,
22747 block: *Block,
22748 src: LazySrcLoc,
22749 operand_ty: Type,
22750 dest_ty: Type,
22751) CompileError!void {
22752 const pt = sema.pt;
22753 const zcu = pt.zcu;
22754 const src_info = operand_ty.ptrInfo(zcu);
22755 const dest_info = dest_ty.ptrInfo(zcu);
22756 const src_child: Type = .fromInterned(src_info.child);
22757 const dest_child: Type = .fromInterned(dest_info.child);
22758
22759 if (block.isComptime() or block.is_typeof) return;
22760 switch (zcu.getTarget().os.tag) {
22761 .vulkan, .opengl => {},
22762 else => return,
22763 }
22764 if (src_info.flags.address_space == .physical_storage_buffer) return;
22765 if (!dest_child.hasRuntimeBits(zcu)) return;
22766
22767 var cur = src_child;
22768 while (cur.toIntern() != dest_info.child) {
22769 cur = switch (cur.zigTypeTag(zcu)) {
22770 .array, .vector => cur.childType(zcu),
22771 .@"struct" => field: {
22772 for (0..cur.structFieldCount(zcu)) |i| {
22773 const field_ty = cur.fieldType(i, zcu);
22774 if (field_ty.hasRuntimeBits(zcu) and cur.structFieldOffset(i, zcu) == 0) break :field field_ty;
22775 }
22776 break :field null;
22777 },
22778 else => null,
22779 } orelse return sema.failWithOwnedErrorMsg(block, msg: {
22780 const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{ operand_ty.fmt(pt), dest_ty.fmt(pt) });
22781 errdefer msg.destroy(sema.gpa);
22782 try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{ dest_child.fmt(pt), src_child.fmt(pt) });
22783 break :msg msg;
22784 });
22785 }
22786}
22787
22788fn checkVectorElemType(
22789 sema: *Sema,
22790 block: *Block,
22791 ty_src: LazySrcLoc,
22792 ty: Type,
22793) CompileError!void {
22794 const pt = sema.pt;
22795 const zcu = pt.zcu;
22796 switch (ty.zigTypeTag(zcu)) {
22797 .int, .float, .bool => return,
22798 .optional, .pointer => if (ty.isPtrAtRuntime(zcu)) return,
22799 else => {},
22800 }
22801 return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{f}'", .{ty.fmt(pt)});
22802}
22803
22804fn checkFloatType(
22805 sema: *Sema,
22806 block: *Block,
22807 ty_src: LazySrcLoc,
22808 ty: Type,
22809) CompileError!void {
22810 const pt = sema.pt;
22811 const zcu = pt.zcu;
22812 switch (ty.zigTypeTag(zcu)) {
22813 .comptime_int, .comptime_float, .float => {},
22814 else => return sema.fail(block, ty_src, "expected float type, found '{f}'", .{ty.fmt(pt)}),
22815 }
22816}
22817
22818fn checkNumericType(
22819 sema: *Sema,
22820 block: *Block,
22821 ty_src: LazySrcLoc,
22822 ty: Type,
22823) CompileError!void {
22824 const pt = sema.pt;
22825 const zcu = pt.zcu;
22826 switch (ty.zigTypeTag(zcu)) {
22827 .comptime_float, .float, .comptime_int, .int => {},
22828 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) {
22829 .comptime_float, .float, .comptime_int, .int => {},
22830 else => |t| return sema.fail(block, ty_src, "expected number, found '{t}'", .{t}),
22831 },
22832 else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}),
22833 }
22834}
22835
22836/// Returns the casted pointer.
22837fn checkAtomicPtrOperand(
22838 sema: *Sema,
22839 block: *Block,
22840 elem_ty: Type,
22841 elem_ty_src: LazySrcLoc,
22842 ptr: Air.Inst.Ref,
22843 ptr_src: LazySrcLoc,
22844 ptr_const: bool,
22845) CompileError!Air.Inst.Ref {
22846 const pt = sema.pt;
22847 const zcu = pt.zcu;
22848 try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .ptr_access);
22849 var diag: Zcu.AtomicPtrAlignmentDiagnostics = .{};
22850 const alignment = zcu.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) {
22851 error.OutOfMemory => |e| return e,
22852 error.FloatTooBig => return sema.fail(
22853 block,
22854 elem_ty_src,
22855 "expected {d}-bit float type or smaller; found {d}-bit float type",
22856 .{ diag.max_bits, diag.bits },
22857 ),
22858 error.IntTooBig => return sema.fail(
22859 block,
22860 elem_ty_src,
22861 "expected {d}-bit integer type or smaller; found {d}-bit integer type",
22862 .{ diag.max_bits, diag.bits },
22863 ),
22864 error.BadType => return sema.fail(
22865 block,
22866 elem_ty_src,
22867 "expected bool, integer, float, enum, packed struct, or pointer type; found '{f}'",
22868 .{elem_ty.fmt(pt)},
22869 ),
22870 };
22871
22872 var wanted_ptr_data: InternPool.Key.PtrType = .{
22873 .child = elem_ty.toIntern(),
22874 .flags = .{
22875 .alignment = alignment,
22876 .is_const = ptr_const,
22877 },
22878 };
22879
22880 const ptr_ty = sema.typeOf(ptr);
22881 const ptr_data = switch (ptr_ty.zigTypeTag(zcu)) {
22882 .pointer => ptr_ty.ptrInfo(zcu),
22883 else => {
22884 const wanted_ptr_ty = try pt.ptrType(wanted_ptr_data);
22885 _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
22886 unreachable;
22887 },
22888 };
22889
22890 wanted_ptr_data.flags.address_space = ptr_data.flags.address_space;
22891 wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero;
22892 wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile;
22893
22894 const wanted_ptr_ty = try pt.ptrType(wanted_ptr_data);
22895 const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src);
22896
22897 return casted_ptr;
22898}
22899
22900fn checkPtrIsNotComptimeMutable(
22901 sema: *Sema,
22902 block: *Block,
22903 ptr_val: Value,
22904 ptr_src: LazySrcLoc,
22905 operand_src: LazySrcLoc,
22906) CompileError!void {
22907 _ = operand_src;
22908 if (sema.isComptimeMutablePtr(ptr_val)) {
22909 return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{});
22910 }
22911}
22912
22913fn checkIntOrVector(
22914 sema: *Sema,
22915 block: *Block,
22916 operand: Air.Inst.Ref,
22917 operand_src: LazySrcLoc,
22918) CompileError!Type {
22919 const pt = sema.pt;
22920 const zcu = pt.zcu;
22921 const operand_ty = sema.typeOf(operand);
22922 switch (operand_ty.zigTypeTag(zcu)) {
22923 .int => return operand_ty,
22924 .vector => {
22925 const elem_ty = operand_ty.childType(zcu);
22926 switch (elem_ty.zigTypeTag(zcu)) {
22927 .int => return elem_ty,
22928 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{
22929 elem_ty.fmt(pt),
22930 }),
22931 }
22932 },
22933 else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{
22934 operand_ty.fmt(pt),
22935 }),
22936 }
22937}
22938
22939fn checkIntOrVectorAllowComptime(
22940 sema: *Sema,
22941 block: *Block,
22942 operand_ty: Type,
22943 operand_src: LazySrcLoc,
22944) CompileError!Type {
22945 const pt = sema.pt;
22946 const zcu = pt.zcu;
22947 switch (operand_ty.zigTypeTag(zcu)) {
22948 .int, .comptime_int => return operand_ty,
22949 .vector => {
22950 const elem_ty = operand_ty.childType(zcu);
22951 switch (elem_ty.zigTypeTag(zcu)) {
22952 .int, .comptime_int => return elem_ty,
22953 else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{
22954 elem_ty.fmt(pt),
22955 }),
22956 }
22957 },
22958 else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{
22959 operand_ty.fmt(pt),
22960 }),
22961 }
22962}
22963
22964const SimdBinOp = struct {
22965 len: ?usize,
22966 /// Coerced to `result_ty`.
22967 lhs: Air.Inst.Ref,
22968 /// Coerced to `result_ty`.
22969 rhs: Air.Inst.Ref,
22970 lhs_val: ?Value,
22971 rhs_val: ?Value,
22972 /// Only different than `scalar_ty` when it is a vector operation.
22973 result_ty: Type,
22974 scalar_ty: Type,
22975};
22976
22977fn checkSimdBinOp(
22978 sema: *Sema,
22979 block: *Block,
22980 src: LazySrcLoc,
22981 uncasted_lhs: Air.Inst.Ref,
22982 uncasted_rhs: Air.Inst.Ref,
22983 lhs_src: LazySrcLoc,
22984 rhs_src: LazySrcLoc,
22985) CompileError!SimdBinOp {
22986 const pt = sema.pt;
22987 const zcu = pt.zcu;
22988 const lhs_ty = sema.typeOf(uncasted_lhs);
22989 const rhs_ty = sema.typeOf(uncasted_rhs);
22990
22991 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
22992 const vec_len: ?usize = if (lhs_ty.zigTypeTag(zcu) == .vector) lhs_ty.vectorLen(zcu) else null;
22993 const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{
22994 .override = &[_]?LazySrcLoc{ lhs_src, rhs_src },
22995 });
22996 const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src);
22997 const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src);
22998
22999 return SimdBinOp{
23000 .len = vec_len,
23001 .lhs = lhs,
23002 .rhs = rhs,
23003 .lhs_val = sema.resolveValue(lhs),
23004 .rhs_val = sema.resolveValue(rhs),
23005 .result_ty = result_ty,
23006 .scalar_ty = result_ty.scalarType(zcu),
23007 };
23008}
23009
23010fn checkVectorizableBinaryOperands(
23011 sema: *Sema,
23012 block: *Block,
23013 src: LazySrcLoc,
23014 lhs_ty: Type,
23015 rhs_ty: Type,
23016 lhs_src: LazySrcLoc,
23017 rhs_src: LazySrcLoc,
23018) CompileError!void {
23019 const pt = sema.pt;
23020 const zcu = pt.zcu;
23021 const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu);
23022 const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu);
23023 if (lhs_zig_ty_tag != .vector and rhs_zig_ty_tag != .vector) return;
23024
23025 const lhs_is_vector = switch (lhs_zig_ty_tag) {
23026 .vector, .array => true,
23027 else => false,
23028 };
23029 const rhs_is_vector = switch (rhs_zig_ty_tag) {
23030 .vector, .array => true,
23031 else => false,
23032 };
23033
23034 if (lhs_is_vector and rhs_is_vector) {
23035 const lhs_len = lhs_ty.arrayLen(zcu);
23036 const rhs_len = rhs_ty.arrayLen(zcu);
23037 if (lhs_len != rhs_len) {
23038 const msg = msg: {
23039 const msg = try sema.errMsg(src, "vector length mismatch", .{});
23040 errdefer msg.destroy(sema.gpa);
23041 try sema.errNote(lhs_src, msg, "length {d} here", .{lhs_len});
23042 try sema.errNote(rhs_src, msg, "length {d} here", .{rhs_len});
23043 break :msg msg;
23044 };
23045 return sema.failWithOwnedErrorMsg(block, msg);
23046 }
23047 } else {
23048 const msg = msg: {
23049 const msg = try sema.errMsg(src, "mixed scalar and vector operands: '{f}' and '{f}'", .{
23050 lhs_ty.fmt(pt), rhs_ty.fmt(pt),
23051 });
23052 errdefer msg.destroy(sema.gpa);
23053 if (lhs_is_vector) {
23054 try sema.errNote(lhs_src, msg, "vector here", .{});
23055 try sema.errNote(rhs_src, msg, "scalar here", .{});
23056 } else {
23057 try sema.errNote(lhs_src, msg, "scalar here", .{});
23058 try sema.errNote(rhs_src, msg, "vector here", .{});
23059 }
23060 break :msg msg;
23061 };
23062 return sema.failWithOwnedErrorMsg(block, msg);
23063 }
23064}
23065
23066fn checkAllScalarsDefined(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) CompileError!void {
23067 const zcu = sema.pt.zcu;
23068 switch (zcu.intern_pool.indexToKey(val.toIntern())) {
23069 .int, .float => {},
23070 .undef => return sema.failWithUseOfUndef(block, src, null),
23071 .aggregate => |agg| {
23072 assert(Type.fromInterned(agg.ty).zigTypeTag(zcu) == .vector);
23073 for (agg.storage.values(), 0..) |elem_val, elem_idx| {
23074 if (Value.fromInterned(elem_val).isUndef(zcu))
23075 return sema.failWithUseOfUndef(block, src, elem_idx);
23076 }
23077 },
23078 else => unreachable,
23079 }
23080}
23081
23082fn resolveExportOptions(
23083 sema: *Sema,
23084 block: *Block,
23085 src: LazySrcLoc,
23086 zir_ref: Zir.Inst.Ref,
23087) CompileError!Zcu.Export.Options {
23088 const pt = sema.pt;
23089 const zcu = pt.zcu;
23090 const comp = zcu.comp;
23091 const gpa = comp.gpa;
23092 const io = comp.io;
23093 const ip = &zcu.intern_pool;
23094
23095 const export_options_ty = try sema.getStdLangType(src, .ExportOptions);
23096 const air_ref = sema.resolveInst(zir_ref);
23097 const options = try sema.coerce(block, export_options_ty, air_ref, src);
23098
23099 const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node });
23100 const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
23101 const section_src = block.src(.{ .init_field_section = src.offset.node_offset_builtin_call_arg.builtin_call_node });
23102 const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node });
23103
23104 const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "name", .no_embedded_nulls), name_src);
23105 const name = try sema.toConstString(block, name_src, name_operand, .{ .simple = .export_options });
23106
23107 const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
23108 const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ .simple = .export_options });
23109 const linkage = try sema.interpretStdLangType(block, linkage_src, linkage_val, std.lang.GlobalLinkage);
23110
23111 const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "section", .no_embedded_nulls), section_src);
23112 const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ .simple = .export_options });
23113 const section = if (section_opt_val.optionalValue(zcu)) |section_val|
23114 try sema.toConstString(block, section_src, Air.internedToRef(section_val.toIntern()), .{ .simple = .export_options })
23115 else
23116 null;
23117
23118 const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
23119 const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{ .simple = .export_options });
23120 const visibility = try sema.interpretStdLangType(block, visibility_src, visibility_val, std.lang.SymbolVisibility);
23121
23122 if (name.len < 1) {
23123 return sema.fail(block, name_src, "exported symbol name cannot be empty", .{});
23124 }
23125
23126 if (visibility != .default and linkage == .internal) {
23127 return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{
23128 name, @tagName(visibility),
23129 });
23130 }
23131
23132 return .{
23133 .name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls),
23134 .linkage = linkage,
23135 .section = try ip.getOrPutStringOpt(gpa, io, pt.tid, section, .no_embedded_nulls),
23136 .visibility = visibility,
23137 };
23138}
23139
23140fn resolveStdLangEnum(
23141 sema: *Sema,
23142 block: *Block,
23143 src: LazySrcLoc,
23144 zir_ref: Zir.Inst.Ref,
23145 comptime name: Zcu.StdLangDecl,
23146 reason: ComptimeReason,
23147) CompileError!@field(std.lang, @tagName(name)) {
23148 const ty = try sema.getStdLangType(src, name);
23149 const air_ref = sema.resolveInst(zir_ref);
23150 const coerced = try sema.coerce(block, ty, air_ref, src);
23151 const val = try sema.resolveConstDefinedValue(block, src, coerced, reason);
23152 return sema.interpretStdLangType(block, src, val, @field(std.lang, @tagName(name)));
23153}
23154
23155fn resolveAtomicOrder(
23156 sema: *Sema,
23157 block: *Block,
23158 src: LazySrcLoc,
23159 zir_ref: Zir.Inst.Ref,
23160 reason: ComptimeReason,
23161) CompileError!std.lang.AtomicOrder {
23162 return sema.resolveStdLangEnum(block, src, zir_ref, .AtomicOrder, reason);
23163}
23164
23165fn resolveAtomicRmwOp(
23166 sema: *Sema,
23167 block: *Block,
23168 src: LazySrcLoc,
23169 zir_ref: Zir.Inst.Ref,
23170) CompileError!std.lang.AtomicRmwOp {
23171 return sema.resolveStdLangEnum(block, src, zir_ref, .AtomicRmwOp, .{ .simple = .operand_atomicRmw_operation });
23172}
23173
23174fn zirCmpxchg(
23175 sema: *Sema,
23176 block: *Block,
23177 extended: Zir.Inst.Extended.InstData,
23178) CompileError!Air.Inst.Ref {
23179 const pt = sema.pt;
23180 const zcu = pt.zcu;
23181 const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data;
23182 const air_tag: Air.Inst.Tag = switch (extended.small) {
23183 0 => .cmpxchg_weak,
23184 1 => .cmpxchg_strong,
23185 else => unreachable,
23186 };
23187 const src = block.nodeOffset(extra.node);
23188 // zig fmt: off
23189 const elem_ty_src = block.builtinCallArgSrc(extra.node, 0);
23190 const ptr_src = block.builtinCallArgSrc(extra.node, 1);
23191 const expected_src = block.builtinCallArgSrc(extra.node, 2);
23192 const new_value_src = block.builtinCallArgSrc(extra.node, 3);
23193 const success_order_src = block.builtinCallArgSrc(extra.node, 4);
23194 const failure_order_src = block.builtinCallArgSrc(extra.node, 5);
23195 // zig fmt: on
23196 const expected_value = sema.resolveInst(extra.expected_value);
23197 const elem_ty = sema.typeOf(expected_value);
23198 if (elem_ty.zigTypeTag(zcu) == .float) {
23199 return sema.fail(
23200 block,
23201 elem_ty_src,
23202 "expected bool, integer, enum, packed struct, or pointer type; found '{f}'",
23203 .{elem_ty.fmt(pt)},
23204 );
23205 }
23206 const uncasted_ptr = sema.resolveInst(extra.ptr);
23207 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
23208 const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src);
23209 const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, .{ .simple = .atomic_order });
23210 const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, .{ .simple = .atomic_order });
23211
23212 if (@backingInt(success_order) < @backingInt(std.lang.AtomicOrder.monotonic)) {
23213 return sema.fail(block, success_order_src, "success atomic ordering must be monotonic or stricter", .{});
23214 }
23215 if (@backingInt(failure_order) < @backingInt(std.lang.AtomicOrder.monotonic)) {
23216 return sema.fail(block, failure_order_src, "failure atomic ordering must be monotonic or stricter", .{});
23217 }
23218 if (@backingInt(failure_order) > @backingInt(success_order)) {
23219 return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{});
23220 }
23221 if (failure_order == .release or failure_order == .acq_rel) {
23222 return sema.fail(block, failure_order_src, "failure atomic ordering must not be release or acq_rel", .{});
23223 }
23224
23225 const result_ty = try pt.optionalType(elem_ty.toIntern());
23226
23227 // special case zero bit types
23228 if (elem_ty.classify(zcu) == .one_possible_value) {
23229 return .fromValue(try pt.nullValue(result_ty));
23230 }
23231
23232 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
23233 if (sema.resolveValue(expected_value)) |expected_val| {
23234 if (sema.resolveValue(new_value)) |new_val| {
23235 if (expected_val.isUndef(zcu) or new_val.isUndef(zcu)) {
23236 // TODO: this should probably cause the memory stored at the pointer
23237 // to become undef as well
23238 return pt.undefRef(result_ty);
23239 }
23240 const ptr_ty = sema.typeOf(ptr);
23241 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
23242 const result_val = try pt.intern(.{ .opt = .{
23243 .ty = result_ty.toIntern(),
23244 .val = if (stored_val.eql(expected_val, elem_ty, zcu)) blk: {
23245 try sema.storePtr(block, src, ptr, new_value);
23246 break :blk .none;
23247 } else stored_val.toIntern(),
23248 } });
23249 return Air.internedToRef(result_val);
23250 } else break :rs new_value_src;
23251 } else break :rs expected_src;
23252 } else ptr_src;
23253
23254 const flags: u32 = @as(u32, @backingInt(success_order)) |
23255 (@as(u32, @backingInt(failure_order)) << 3);
23256
23257 try sema.requireRuntimeBlock(block, src, runtime_src);
23258 return block.addInst(.{
23259 .tag = air_tag,
23260 .data = .{ .ty_pl = .{
23261 .ty = result_ty,
23262 .payload = try sema.addExtra(Air.Cmpxchg{
23263 .ptr = ptr,
23264 .expected_value = expected_value,
23265 .new_value = new_value,
23266 .flags = flags,
23267 }),
23268 } },
23269 });
23270}
23271
23272fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
23273 const pt = sema.pt;
23274 const zcu = pt.zcu;
23275 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
23276 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
23277 const src = block.nodeOffset(inst_data.src_node);
23278 const scalar_src = block.builtinCallArgSrc(inst_data.src_node, 0);
23279 const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat");
23280
23281 switch (dest_ty.zigTypeTag(zcu)) {
23282 .array, .vector => {},
23283 else => return sema.fail(block, src, "expected array or vector type, found '{f}'", .{dest_ty.fmt(pt)}),
23284 }
23285
23286 const operand = sema.resolveInst(extra.rhs);
23287 const scalar_ty = dest_ty.childType(zcu);
23288 const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src);
23289
23290 const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu));
23291
23292 // If the length is 0, the result is comptime-known even if the operand isn't.
23293 if (len == 0) return .fromValue(try pt.aggregateValue(dest_ty, &.{}));
23294
23295 const maybe_sentinel = dest_ty.sentinel(zcu);
23296
23297 if (sema.resolveValue(scalar)) |scalar_val| {
23298 full: {
23299 if (dest_ty.zigTypeTag(zcu) == .vector) break :full;
23300 const sentinel = maybe_sentinel orelse break :full;
23301 if (sentinel.toIntern() == scalar_val.toIntern()) break :full;
23302 // This is a array with non-zero length and a sentinel which does not match the element.
23303 // We have to use the full `elems` representation.
23304 const elems = try sema.arena.alloc(InternPool.Index, len + 1);
23305 @memset(elems[0..len], scalar_val.toIntern());
23306 elems[len] = sentinel.toIntern();
23307 return .fromValue(try pt.aggregateValue(dest_ty, elems));
23308 }
23309 return .fromValue(try pt.aggregateSplatValue(dest_ty, scalar_val));
23310 }
23311
23312 try sema.requireRuntimeBlock(block, src, scalar_src);
23313
23314 switch (dest_ty.zigTypeTag(zcu)) {
23315 .vector, .array => return block.addTyOp(.splat, dest_ty, scalar),
23316 else => unreachable,
23317 }
23318}
23319
23320fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
23321 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
23322 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
23323 const op_src = block.builtinCallArgSrc(inst_data.src_node, 0);
23324 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 1);
23325 const operation = try sema.resolveStdLangEnum(block, op_src, extra.lhs, .ReduceOp, .{ .simple = .operand_reduce_operation });
23326 const operand = sema.resolveInst(extra.rhs);
23327 const operand_ty = sema.typeOf(operand);
23328 const pt = sema.pt;
23329 const zcu = pt.zcu;
23330
23331 if (operand_ty.zigTypeTag(zcu) != .vector) {
23332 return sema.fail(block, operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)});
23333 }
23334
23335 const scalar_ty = operand_ty.childType(zcu);
23336
23337 // Type-check depending on operation.
23338 switch (operation) {
23339 .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) {
23340 .int, .bool => {},
23341 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{f}'", .{
23342 @tagName(operation), operand_ty.fmt(pt),
23343 }),
23344 },
23345 .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(zcu)) {
23346 .int, .float => {},
23347 else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{f}'", .{
23348 @tagName(operation), operand_ty.fmt(pt),
23349 }),
23350 },
23351 }
23352
23353 const vec_len = operand_ty.vectorLen(zcu);
23354 if (vec_len == 0) {
23355 // TODO re-evaluate if we should introduce a "neutral value" for some operations,
23356 // e.g. zero for add and one for mul.
23357 return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{});
23358 }
23359
23360 if (sema.resolveValue(operand)) |operand_val| {
23361 if (operand_val.isUndef(zcu)) return pt.undefRef(scalar_ty);
23362
23363 var accum: Value = try operand_val.elemValue(pt, 0);
23364 var i: u32 = 1;
23365 while (i < vec_len) : (i += 1) {
23366 const elem_val = try operand_val.elemValue(pt, i);
23367 accum = switch (operation) {
23368 // zig fmt: off
23369 .And => try arith.bitwiseBin (sema, scalar_ty, accum, elem_val, .@"and"),
23370 .Or => try arith.bitwiseBin (sema, scalar_ty, accum, elem_val, .@"or"),
23371 .Xor => try arith.bitwiseBin (sema, scalar_ty, accum, elem_val, .xor),
23372 .Min => Value.numberMin ( accum, elem_val, zcu),
23373 .Max => Value.numberMax ( accum, elem_val, zcu),
23374 .Add => try arith.addMaybeWrap(sema, scalar_ty, accum, elem_val),
23375 .Mul => try arith.mulMaybeWrap(sema, scalar_ty, accum, elem_val),
23376 // zig fmt: on
23377 };
23378 }
23379 return Air.internedToRef(accum.toIntern());
23380 }
23381
23382 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src);
23383 return block.addReduce(operand, operation);
23384}
23385
23386fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
23387 const pt = sema.pt;
23388 const zcu = pt.zcu;
23389 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
23390 const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
23391 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
23392 const mask_src = block.builtinCallArgSrc(inst_data.src_node, 3);
23393
23394 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
23395 try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
23396 const a = sema.resolveInst(extra.a);
23397 const b = sema.resolveInst(extra.b);
23398 var mask = sema.resolveInst(extra.mask);
23399 var mask_ty = sema.typeOf(mask);
23400
23401 const mask_len = switch (sema.typeOf(mask).zigTypeTag(zcu)) {
23402 .array, .vector => sema.typeOf(mask).arrayLen(zcu),
23403 else => return sema.fail(block, mask_src, "expected vector or array, found '{f}'", .{sema.typeOf(mask).fmt(pt)}),
23404 };
23405 mask_ty = try pt.vectorType(.{
23406 .len = @intCast(mask_len),
23407 .child = .i32_type,
23408 });
23409 mask = try sema.coerce(block, mask_ty, mask, mask_src);
23410 const mask_val = try sema.resolveConstValue(block, mask_src, mask, .{ .simple = .operand_shuffle_mask });
23411 return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(mask_len));
23412}
23413
23414fn analyzeShuffle(
23415 sema: *Sema,
23416 block: *Block,
23417 src_node: std.zig.Ast.Node.Offset,
23418 elem_ty: Type,
23419 a_uncoerced: Air.Inst.Ref,
23420 b_uncoerced: Air.Inst.Ref,
23421 mask: Value,
23422 mask_len: u32,
23423) CompileError!Air.Inst.Ref {
23424 const pt = sema.pt;
23425 const zcu = pt.zcu;
23426 const a_src = block.builtinCallArgSrc(src_node, 1);
23427 const b_src = block.builtinCallArgSrc(src_node, 2);
23428 const mask_src = block.builtinCallArgSrc(src_node, 3);
23429
23430 // If the type of `a` is `@TypeOf(undefined)`, i.e. the argument is untyped,
23431 // this is 0, because it is an error to index into this vector.
23432 const a_len: u32 = switch (sema.typeOf(a_uncoerced).zigTypeTag(zcu)) {
23433 .array, .vector => @intCast(sema.typeOf(a_uncoerced).arrayLen(zcu)),
23434 .undefined => 0,
23435 else => return sema.fail(block, a_src, "expected vector of '{f}', found '{f}'", .{
23436 elem_ty.fmt(pt), sema.typeOf(a_uncoerced).fmt(pt),
23437 }),
23438 };
23439 const a_ty = try pt.vectorType(.{ .len = a_len, .child = elem_ty.toIntern() });
23440 const a_coerced = try sema.coerce(block, a_ty, a_uncoerced, a_src);
23441
23442 // If the type of `b` is `@TypeOf(undefined)`, i.e. the argument is untyped, this is 0, because it is an error to index into this vector.
23443 const b_len: u32 = switch (sema.typeOf(b_uncoerced).zigTypeTag(zcu)) {
23444 .array, .vector => @intCast(sema.typeOf(b_uncoerced).arrayLen(zcu)),
23445 .undefined => 0,
23446 else => return sema.fail(block, b_src, "expected vector of '{f}', found '{f}'", .{
23447 elem_ty.fmt(pt), sema.typeOf(b_uncoerced).fmt(pt),
23448 }),
23449 };
23450 const b_ty = try pt.vectorType(.{ .len = b_len, .child = elem_ty.toIntern() });
23451 const b_coerced = try sema.coerce(block, b_ty, b_uncoerced, b_src);
23452
23453 const result_ty = try pt.vectorType(.{ .len = mask_len, .child = elem_ty.toIntern() });
23454
23455 // We're going to pre-emptively reserve space in `sema.air_extra`. The reason for this is we need
23456 // a `u32` buffer of length `mask_len` anyway, and putting it in `sema.air_extra` avoids a copy
23457 // in the runtime case. If the result is comptime-known, we'll shrink `air_extra` back.
23458 const air_extra_idx: u32 = @intCast(sema.air_extra.items.len);
23459 const air_mask_buf = try sema.air_extra.addManyAsSlice(sema.gpa, mask_len);
23460
23461 // We want to interpret that buffer in `air_extra` in a few ways. Initially, we'll consider its
23462 // elements as `Air.Inst.ShuffleTwoMask`, essentially representing the raw mask values; then, we'll
23463 // convert it to `InternPool.Index` or `Air.Inst.ShuffleOneMask` if there are comptime-known operands.
23464 const mask_ip_index: []InternPool.Index = @ptrCast(air_mask_buf);
23465 const mask_shuffle_one: []Air.ShuffleOneMask = @ptrCast(air_mask_buf);
23466 const mask_shuffle_two: []Air.ShuffleTwoMask = @ptrCast(air_mask_buf);
23467
23468 // Initial loop: check mask elements, populate `mask_shuffle_two`.
23469 var a_used = false;
23470 var b_used = false;
23471 for (mask_shuffle_two, 0..mask_len) |*out, mask_idx| {
23472 const mask_val = try mask.elemValue(pt, mask_idx);
23473 if (mask_val.isUndef(zcu)) {
23474 out.* = .undef;
23475 continue;
23476 }
23477 // Safe because mask elements are `i32` and we already checked for undef:
23478 const raw = mask_val.toSignedInt(zcu);
23479 if (raw >= 0) {
23480 const idx: u32 = @intCast(raw);
23481 a_used = true;
23482 out.* = .aElem(idx);
23483 if (idx >= a_len) return sema.failWithOwnedErrorMsg(block, msg: {
23484 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx});
23485 errdefer msg.destroy(sema.gpa);
23486 try sema.errNote(a_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, a_ty.fmt(pt) });
23487 if (idx < b_len) {
23488 try sema.errNote(b_src, msg, "use '~@as(u32, {d})' to index into second vector given here", .{idx});
23489 }
23490 break :msg msg;
23491 });
23492 } else {
23493 const idx: u32 = @intCast(~raw);
23494 b_used = true;
23495 out.* = .bElem(idx);
23496 if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: {
23497 const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx});
23498 errdefer msg.destroy(sema.gpa);
23499 try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, b_ty.fmt(pt) });
23500 break :msg msg;
23501 });
23502 }
23503 }
23504
23505 const maybe_a_val = sema.resolveValue(a_coerced);
23506 const maybe_b_val = sema.resolveValue(b_coerced);
23507
23508 const a_rt = a_used and maybe_a_val == null;
23509 const b_rt = b_used and maybe_b_val == null;
23510
23511 if (a_rt and b_rt) {
23512 // Both operands are needed and runtime-known. We need a `[]ShuffleTwomask`... which is
23513 // exactly what we already have in `mask_shuffle_two`! So, we're basically done already.
23514 // We just need to append the two operands.
23515 try sema.air_extra.ensureUnusedCapacity(sema.gpa, 2);
23516 sema.appendRefsAssumeCapacity(&.{ a_coerced, b_coerced });
23517 return block.addInst(.{
23518 .tag = .shuffle_two,
23519 .data = .{ .ty_pl = .{
23520 .ty = result_ty,
23521 .payload = air_extra_idx,
23522 } },
23523 });
23524 } else if (a_rt) {
23525 // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`.
23526 for (mask_shuffle_two, mask_shuffle_one) |in, *out| {
23527 out.* = switch (in.unwrap()) {
23528 .undef => .value(try pt.undefValue(elem_ty)),
23529 .a_elem => |idx| .elem(idx),
23530 .b_elem => |idx| .value(try maybe_b_val.?.elemValue(pt, idx)),
23531 };
23532 }
23533 // Now just append our single runtime operand, and we're done.
23534 try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1);
23535 sema.appendRefsAssumeCapacity(&.{a_coerced});
23536 return block.addInst(.{
23537 .tag = .shuffle_one,
23538 .data = .{ .ty_pl = .{
23539 .ty = result_ty,
23540 .payload = air_extra_idx,
23541 } },
23542 });
23543 } else if (b_rt) {
23544 // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`.
23545 for (mask_shuffle_two, mask_shuffle_one) |in, *out| {
23546 out.* = switch (in.unwrap()) {
23547 .undef => .value(try pt.undefValue(elem_ty)),
23548 .a_elem => |idx| .value(try maybe_a_val.?.elemValue(pt, idx)),
23549 .b_elem => |idx| .elem(idx),
23550 };
23551 }
23552 // Now just append our single runtime operand, and we're done.
23553 try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1);
23554 sema.appendRefsAssumeCapacity(&.{b_coerced});
23555 return block.addInst(.{
23556 .tag = .shuffle_one,
23557 .data = .{ .ty_pl = .{
23558 .ty = result_ty,
23559 .payload = air_extra_idx,
23560 } },
23561 });
23562 } else {
23563 // The result will be comptime-known. We must convert the `ShuffleTwoMask` values to
23564 // `InternPool.Index` values using the known operands.
23565 for (mask_shuffle_two, mask_ip_index) |in, *out| {
23566 const val: Value = switch (in.unwrap()) {
23567 // Special case zero bit types: there is no undefined value for OPV elements.
23568 // Only affects the case where `!a_rt and !b_rt` since `a_coerced` and `b_coerced`'s types are also OPV for OPV elements.
23569 .undef => try elem_ty.onePossibleValue(pt) orelse try pt.undefValue(elem_ty),
23570 .a_elem => |idx| try maybe_a_val.?.elemValue(pt, idx),
23571 .b_elem => |idx| try maybe_b_val.?.elemValue(pt, idx),
23572 };
23573 out.* = val.toIntern();
23574 }
23575 const res = try pt.aggregateValue(result_ty, mask_ip_index);
23576 // We have a comptime-known result, so didn't need `air_mask_buf` -- remove it from `sema.air_extra`.
23577 assert(sema.air_extra.items.len == air_extra_idx + air_mask_buf.len);
23578 sema.air_extra.shrinkRetainingCapacity(air_extra_idx);
23579 return Air.internedToRef(res.toIntern());
23580 }
23581}
23582
23583fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
23584 const pt = sema.pt;
23585 const zcu = pt.zcu;
23586 const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data;
23587
23588 const src = block.nodeOffset(extra.node);
23589 const elem_ty_src = block.builtinCallArgSrc(extra.node, 0);
23590 const pred_src = block.builtinCallArgSrc(extra.node, 1);
23591 const a_src = block.builtinCallArgSrc(extra.node, 2);
23592 const b_src = block.builtinCallArgSrc(extra.node, 3);
23593
23594 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
23595 try sema.checkVectorElemType(block, elem_ty_src, elem_ty);
23596 const pred_uncoerced = sema.resolveInst(extra.pred);
23597 const pred_ty = sema.typeOf(pred_uncoerced);
23598
23599 const vec_len_u64 = switch (pred_ty.zigTypeTag(zcu)) {
23600 .vector, .array => pred_ty.arrayLen(zcu),
23601 else => return sema.fail(block, pred_src, "expected vector or array, found '{f}'", .{pred_ty.fmt(pt)}),
23602 };
23603 const vec_len: u32 = @intCast(try sema.usizeCast(block, pred_src, vec_len_u64));
23604
23605 const bool_vec_ty = try pt.vectorType(.{
23606 .len = vec_len,
23607 .child = .bool_type,
23608 });
23609 const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src);
23610
23611 const vec_ty = try pt.vectorType(.{
23612 .len = vec_len,
23613 .child = elem_ty.toIntern(),
23614 });
23615 const a = try sema.coerce(block, vec_ty, sema.resolveInst(extra.a), a_src);
23616 const b = try sema.coerce(block, vec_ty, sema.resolveInst(extra.b), b_src);
23617
23618 // special case zero bit types
23619 if (try vec_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
23620
23621 const maybe_pred = sema.resolveValue(pred);
23622 const maybe_a = sema.resolveValue(a);
23623 const maybe_b = sema.resolveValue(b);
23624
23625 const runtime_src = if (maybe_pred) |pred_val| rs: {
23626 if (pred_val.isUndef(zcu)) return pt.undefRef(vec_ty);
23627
23628 if (maybe_a) |a_val| {
23629 if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty);
23630
23631 if (maybe_b) |b_val| {
23632 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty);
23633
23634 const elems = try sema.gpa.alloc(InternPool.Index, vec_len);
23635 defer sema.gpa.free(elems);
23636 for (elems, 0..) |*elem, i| {
23637 const pred_elem_val = try pred_val.elemValue(pt, i);
23638 const should_choose_a = pred_elem_val.toBool();
23639 elem.* = (try (if (should_choose_a) a_val else b_val).elemValue(pt, i)).toIntern();
23640 }
23641
23642 return Air.internedToRef((try pt.aggregateValue(vec_ty, elems)).toIntern());
23643 } else {
23644 break :rs b_src;
23645 }
23646 } else {
23647 if (maybe_b) |b_val| {
23648 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty);
23649 }
23650 break :rs a_src;
23651 }
23652 } else rs: {
23653 if (maybe_a) |a_val| {
23654 if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty);
23655 }
23656 if (maybe_b) |b_val| {
23657 if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty);
23658 }
23659 break :rs pred_src;
23660 };
23661
23662 try sema.requireRuntimeBlock(block, src, runtime_src);
23663 return block.addInst(.{
23664 .tag = .select,
23665 .data = .{ .pl_op = .{
23666 .operand = pred,
23667 .payload = try block.sema.addExtra(Air.Bin{
23668 .lhs = a,
23669 .rhs = b,
23670 }),
23671 } },
23672 });
23673}
23674
23675fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
23676 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
23677 const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
23678 // zig fmt: off
23679 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
23680 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1);
23681 const order_src = block.builtinCallArgSrc(inst_data.src_node, 2);
23682 // zig fmt: on
23683 const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type);
23684 const uncasted_ptr = sema.resolveInst(extra.ptr);
23685 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true);
23686 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
23687
23688 try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .ptr_access);
23689
23690 switch (order) {
23691 .release, .acq_rel => {
23692 return sema.fail(
23693 block,
23694 order_src,
23695 "@atomicLoad atomic ordering must not be release or acq_rel",
23696 .{},
23697 );
23698 },
23699 else => {},
23700 }
23701
23702 if (try elem_ty.onePossibleValue(sema.pt)) |opv| return .fromValue(opv);
23703
23704 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
23705 if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| {
23706 return Air.internedToRef(elem_val.toIntern());
23707 }
23708 }
23709
23710 try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src);
23711 return block.addInst(.{
23712 .tag = .atomic_load,
23713 .data = .{ .atomic_load = .{
23714 .ptr = ptr,
23715 .order = order,
23716 } },
23717 });
23718}
23719
23720fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
23721 const pt = sema.pt;
23722 const zcu = pt.zcu;
23723 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
23724 const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
23725 const src = block.nodeOffset(inst_data.src_node);
23726 // zig fmt: off
23727 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
23728 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1);
23729 const op_src = block.builtinCallArgSrc(inst_data.src_node, 2);
23730 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 3);
23731 const order_src = block.builtinCallArgSrc(inst_data.src_node, 4);
23732 // zig fmt: on
23733 const operand = sema.resolveInst(extra.operand);
23734 const elem_ty = sema.typeOf(operand);
23735 const uncasted_ptr = sema.resolveInst(extra.ptr);
23736 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
23737 const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation);
23738
23739 switch (elem_ty.zigTypeTag(zcu)) {
23740 .@"enum" => if (op != .Xchg) {
23741 return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{});
23742 },
23743 .bool => if (op != .Xchg) {
23744 return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{});
23745 },
23746 .float => switch (op) {
23747 .Xchg, .Add, .Sub, .Max, .Min => {},
23748 else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}),
23749 },
23750 else => {},
23751 }
23752 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
23753
23754 if (order == .unordered) {
23755 return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be unordered", .{});
23756 }
23757
23758 // special case zero bit types
23759 if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
23760
23761 const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: {
23762 const maybe_operand_val = sema.resolveValue(operand);
23763 const operand_val = maybe_operand_val orelse {
23764 try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src);
23765 break :rs operand_src;
23766 };
23767 if (sema.isComptimeMutablePtr(ptr_val)) {
23768 const ptr_ty = sema.typeOf(ptr);
23769 const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src;
23770 const new_val = switch (op) {
23771 // zig fmt: off
23772 .Xchg => operand_val,
23773 .Add => try arith.addMaybeWrap(sema, elem_ty, stored_val, operand_val),
23774 .Sub => try arith.subMaybeWrap(sema, elem_ty, stored_val, operand_val),
23775 .And => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .@"and"),
23776 .Nand => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .nand),
23777 .Or => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .@"or"),
23778 .Xor => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .xor),
23779 .Max => Value.numberMax ( stored_val, operand_val, zcu),
23780 .Min => Value.numberMin ( stored_val, operand_val, zcu),
23781 // zig fmt: on
23782 };
23783 try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty);
23784 return Air.internedToRef(stored_val.toIntern());
23785 } else break :rs ptr_src;
23786 } else ptr_src;
23787
23788 const flags: u32 = @as(u32, @backingInt(order)) | (@as(u32, @backingInt(op)) << 3);
23789
23790 try sema.requireRuntimeBlock(block, src, runtime_src);
23791 return block.addInst(.{
23792 .tag = .atomic_rmw,
23793 .data = .{ .pl_op = .{
23794 .operand = ptr,
23795 .payload = try sema.addExtra(Air.AtomicRmw{
23796 .operand = operand,
23797 .flags = flags,
23798 }),
23799 } },
23800 });
23801}
23802
23803fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
23804 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
23805 const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
23806 const src = block.nodeOffset(inst_data.src_node);
23807 // zig fmt: off
23808 const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0);
23809 const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1);
23810 const operand_src = block.builtinCallArgSrc(inst_data.src_node, 2);
23811 const order_src = block.builtinCallArgSrc(inst_data.src_node, 3);
23812 // zig fmt: on
23813 const operand = sema.resolveInst(extra.operand);
23814 const elem_ty = sema.typeOf(operand);
23815 const uncasted_ptr = sema.resolveInst(extra.ptr);
23816 const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false);
23817 const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order });
23818
23819 const air_tag: Air.Inst.Tag = switch (order) {
23820 .acquire, .acq_rel => {
23821 return sema.fail(
23822 block,
23823 order_src,
23824 "@atomicStore atomic ordering must not be acquire or acq_rel",
23825 .{},
23826 );
23827 },
23828 .unordered => .atomic_store_unordered,
23829 .monotonic => .atomic_store_monotonic,
23830 .release => .atomic_store_release,
23831 .seq_cst => .atomic_store_seq_cst,
23832 };
23833
23834 return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag);
23835}
23836
23837fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
23838 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
23839 const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
23840 const src = block.nodeOffset(inst_data.src_node);
23841
23842 const mulend1_src = block.builtinCallArgSrc(inst_data.src_node, 1);
23843 const mulend2_src = block.builtinCallArgSrc(inst_data.src_node, 2);
23844 const addend_src = block.builtinCallArgSrc(inst_data.src_node, 3);
23845
23846 const addend = sema.resolveInst(extra.addend);
23847 const ty = sema.typeOf(addend);
23848 const mulend1 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend1), mulend1_src);
23849 const mulend2 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend2), mulend2_src);
23850
23851 const maybe_mulend1 = sema.resolveValue(mulend1);
23852 const maybe_mulend2 = sema.resolveValue(mulend2);
23853 const maybe_addend = sema.resolveValue(addend);
23854 const pt = sema.pt;
23855 const zcu = pt.zcu;
23856
23857 switch (ty.scalarType(zcu).zigTypeTag(zcu)) {
23858 .comptime_float, .float => {},
23859 else => return sema.fail(block, src, "expected vector of floats or float type, found '{f}'", .{ty.fmt(pt)}),
23860 }
23861
23862 const runtime_src = if (maybe_mulend1) |mulend1_val| rs: {
23863 if (maybe_mulend2) |mulend2_val| {
23864 if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty);
23865
23866 if (maybe_addend) |addend_val| {
23867 if (addend_val.isUndef(zcu)) return pt.undefRef(ty);
23868 const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, pt);
23869 return Air.internedToRef(result_val.toIntern());
23870 } else {
23871 break :rs addend_src;
23872 }
23873 } else {
23874 if (maybe_addend) |addend_val| {
23875 if (addend_val.isUndef(zcu)) return pt.undefRef(ty);
23876 }
23877 break :rs mulend2_src;
23878 }
23879 } else rs: {
23880 if (maybe_mulend2) |mulend2_val| {
23881 if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty);
23882 }
23883 if (maybe_addend) |addend_val| {
23884 if (addend_val.isUndef(zcu)) return pt.undefRef(ty);
23885 }
23886 break :rs mulend1_src;
23887 };
23888
23889 try sema.requireRuntimeBlock(block, src, runtime_src);
23890 return block.addInst(.{
23891 .tag = .mul_add,
23892 .data = .{ .pl_op = .{
23893 .operand = addend,
23894 .payload = try sema.addExtra(Air.Bin{
23895 .lhs = mulend1,
23896 .rhs = mulend2,
23897 }),
23898 } },
23899 });
23900}
23901
23902fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
23903 const pt = sema.pt;
23904 const zcu = pt.zcu;
23905 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
23906 const modifier_src = block.builtinCallArgSrc(inst_data.src_node, 0);
23907 const func_src = block.builtinCallArgSrc(inst_data.src_node, 1);
23908 const args_src = block.builtinCallArgSrc(inst_data.src_node, 2);
23909 const call_src = block.nodeOffset(inst_data.src_node);
23910
23911 const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
23912 const func = sema.resolveInst(extra.callee);
23913
23914 const modifier_ty = try sema.getStdLangType(call_src, .CallModifier);
23915 const air_ref = sema.resolveInst(extra.modifier);
23916 const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src);
23917 const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{ .simple = .call_modifier });
23918 var modifier = try sema.interpretStdLangType(block, modifier_src, modifier_val, std.lang.CallModifier);
23919 switch (modifier) {
23920 // These can be upgraded to comptime or nosuspend calls.
23921 .auto, .never_tail, .no_suspend => {
23922 if (block.isComptime()) {
23923 if (modifier == .never_tail) {
23924 return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{});
23925 }
23926 modifier = .compile_time;
23927 } else if (extra.flags.is_nosuspend) {
23928 modifier = .no_suspend;
23929 }
23930 },
23931 // These can be upgraded to comptime. nosuspend bit can be safely ignored.
23932 .always_inline, .compile_time => {
23933 _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse {
23934 return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)});
23935 };
23936
23937 if (block.isComptime()) {
23938 modifier = .compile_time;
23939 }
23940 },
23941 .always_tail => {
23942 if (block.isComptime()) {
23943 modifier = .compile_time;
23944 }
23945 },
23946 .never_inline => {
23947 if (block.isComptime()) {
23948 return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{});
23949 }
23950 },
23951 }
23952
23953 const args = sema.resolveInst(extra.args);
23954
23955 const args_ty = sema.typeOf(args);
23956 if (!args_ty.isTuple(zcu)) {
23957 return sema.fail(block, args_src, "expected a tuple, found '{f}'", .{args_ty.fmt(pt)});
23958 }
23959
23960 const resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(zcu));
23961 for (resolved_args, 0..) |*resolved, i| {
23962 resolved.* = try sema.tupleFieldValByIndex(block, args, @intCast(i), args_ty);
23963 }
23964
23965 const callee_ty = sema.typeOf(func);
23966 const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false);
23967 const ensure_result_used = extra.flags.ensure_result_used;
23968 return sema.analyzeCall(
23969 block,
23970 func,
23971 func_ty,
23972 func_src,
23973 call_src,
23974 modifier,
23975 ensure_result_used,
23976 .{ .call_builtin = .{
23977 .call_node_offset = inst_data.src_node,
23978 .args = resolved_args,
23979 } },
23980 null,
23981 .@"@call",
23982 );
23983}
23984
23985fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
23986 const pt = sema.pt;
23987 const zcu = pt.zcu;
23988 const ip = &zcu.intern_pool;
23989
23990 const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, extended.operand).data;
23991 const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
23992 const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small)));
23993 assert(!flags.ptr_cast);
23994 const inst_src = block.nodeOffset(extra.src_node);
23995 const field_name_src = block.builtinCallArgSrc(extra.src_node, 0);
23996 const field_ptr_src = block.builtinCallArgSrc(extra.src_node, 1);
23997
23998 const maybe_opt_parent_ptr_ty = try sema.resolveDestType(block, inst_src, extra.parent_ptr_type, .remove_eu, "@fieldParentPtr");
23999 try sema.checkPtrType(block, inst_src, maybe_opt_parent_ptr_ty, true);
24000 const parent_ptr_ty = switch (maybe_opt_parent_ptr_ty.zigTypeTag(zcu)) {
24001 .optional => maybe_opt_parent_ptr_ty.optionalChild(zcu),
24002 .pointer => maybe_opt_parent_ptr_ty,
24003 else => unreachable,
24004 };
24005 const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu);
24006 if (parent_ptr_info.flags.size != .one) {
24007 return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)});
24008 }
24009 const parent_ty: Type = .fromInterned(parent_ptr_info.child);
24010 try sema.ensureLayoutResolved(parent_ty, inst_src, .field_used);
24011 switch (parent_ty.zigTypeTag(zcu)) {
24012 .@"struct", .@"union" => {},
24013 else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}),
24014 }
24015
24016 const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name });
24017 const field_index = switch (parent_ty.zigTypeTag(zcu)) {
24018 .@"struct" => blk: {
24019 if (parent_ty.isTuple(zcu)) {
24020 if (field_name.eqlSlice("len", ip)) {
24021 return sema.fail(block, inst_src, "cannot get @fieldParentPtr of 'len' field of tuple", .{});
24022 }
24023 break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, field_name_src);
24024 } else {
24025 break :blk try sema.structFieldIndex(block, parent_ty, field_name, field_name_src);
24026 }
24027 },
24028 .@"union" => try sema.unionFieldIndex(block, parent_ty, field_name, field_name_src),
24029 else => unreachable,
24030 };
24031 if (parent_ty.zigTypeTag(zcu) == .@"struct" and parent_ty.structFieldIsComptime(field_index, zcu)) {
24032 return sema.fail(block, field_name_src, "cannot get @fieldParentPtr of a comptime field", .{});
24033 }
24034
24035 const field_ptr = sema.resolveInst(extra.field_ptr);
24036 const field_ptr_ty = sema.typeOf(field_ptr);
24037 try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty);
24038
24039 const hypothetical_field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_index, pt);
24040 const casted_field_ptr = try sema.ptrCastFull(
24041 block,
24042 flags,
24043 inst_src,
24044 field_ptr,
24045 field_ptr_src,
24046 hypothetical_field_ptr_ty,
24047 "@fieldParentPtr",
24048 );
24049
24050 const unaligned_parent_ptr_ty = try pt.ptrType(info: {
24051 var info = parent_ptr_info;
24052 info.flags.alignment = hypothetical_field_ptr_ty.ptrAlignment(zcu);
24053 break :info info;
24054 });
24055
24056 const unaligned_parent_ptr: Air.Inst.Ref = if (try sema.resolveDefinedValue(
24057 block,
24058 field_ptr_src,
24059 casted_field_ptr,
24060 )) |field_ptr_val| switch (parent_ty.containerLayout(zcu)) {
24061 .@"packed" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)),
24062 .@"extern" => switch (parent_ty.zigTypeTag(zcu)) {
24063 .@"struct" => .fromValue(try sema.ptrSubtract(
24064 block,
24065 field_ptr_src,
24066 field_ptr_val,
24067 parent_ty.structFieldOffset(field_index, zcu),
24068 unaligned_parent_ptr_ty,
24069 )),
24070 .@"union" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)),
24071 else => unreachable,
24072 },
24073 .auto => result: {
24074 const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: {
24075 const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) {
24076 .ptr => |ptr| ptr,
24077 else => break :opt_field null,
24078 };
24079 if (ptr.byte_offset != 0) break :opt_field null;
24080 break :opt_field switch (ptr.base_addr) {
24081 .field => |field| field,
24082 else => null,
24083 };
24084 };
24085
24086 const field = opt_field orelse {
24087 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
24088 };
24089
24090 if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) {
24091 return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{});
24092 }
24093
24094 if (field.index != field_index) {
24095 return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{
24096 field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt),
24097 });
24098 }
24099 break :result .fromValue(try pt.getCoerced(.fromInterned(field.base), unaligned_parent_ptr_ty));
24100 },
24101 } else result: {
24102 break :result try block.addInst(.{
24103 .tag = .field_parent_ptr,
24104 .data = .{ .ty_pl = .{
24105 .ty = unaligned_parent_ptr_ty,
24106 .payload = try block.sema.addExtra(Air.FieldParentPtr{
24107 .field_ptr = casted_field_ptr,
24108 .field_index = @intCast(field_index),
24109 }),
24110 } },
24111 });
24112 };
24113
24114 // There's one more error condition: if the hypothetical field pointer type has a lower
24115 // alignment than the parent pointer type, then we need an `@alignCast`. Note that the earlier
24116 // `ptrCastFull` may *also* have "used" the `@alignCast`; that would be a case where the field
24117 // is naturally less aligned than the rest of the struct, *and* the field pointer is itself
24118 // underaligned compared to the field alignment. For example, `struct { a: u32, b: u16 }` with
24119 // a field pointer of type `*align(1) u16`.
24120 switch (hypothetical_field_ptr_ty.ptrAlignment(zcu).order(parent_ptr_ty.ptrAlignment(zcu))) {
24121 .gt => unreachable, // getting a field pointer can never increase alignment
24122 .eq => return sema.coerce(block, maybe_opt_parent_ptr_ty, unaligned_parent_ptr, inst_src),
24123 .lt => if (flags.align_cast) {
24124 // Go through `ptrCastFull` for the safety check.
24125 return sema.ptrCastFull(
24126 block,
24127 flags,
24128 inst_src,
24129 unaligned_parent_ptr,
24130 inst_src,
24131 maybe_opt_parent_ptr_ty,
24132 "@fieldParentPtr",
24133 );
24134 } else return sema.failWithOwnedErrorMsg(block, msg: {
24135 const msg = try sema.errMsg(inst_src, "@fieldParentPtr increases pointer alignment", .{});
24136 errdefer msg.destroy(sema.gpa);
24137 try sema.errNote(inst_src, msg, "parent pointer type '{f}' has alignment '{d}'", .{
24138 parent_ptr_ty.fmt(pt),
24139 parent_ptr_ty.abiAlignment(zcu),
24140 });
24141 if (parent_ty.isTuple(zcu)) {
24142 try sema.errNote(field_ptr_src, msg, "tuple field '{d}' limits alignment to '{d}'", .{
24143 field_index,
24144 field_ptr_ty.ptrAlignment(zcu),
24145 });
24146 } else {
24147 try sema.errNote(parent_ty.srcLoc(zcu), msg, "{t} field '{f}' limits alignment to '{d}'", .{
24148 parent_ty.zigTypeTag(zcu),
24149 switch (parent_ty.zigTypeTag(zcu)) {
24150 .@"struct" => parent_ty.structFieldName(field_index, zcu).unwrap().?.fmt(ip),
24151 .@"union" => parent_ty.unionTagTypeHypothetical(zcu).enumFieldName(field_index, zcu).fmt(ip),
24152 else => unreachable,
24153 },
24154 field_ptr_ty.ptrAlignment(zcu),
24155 });
24156 }
24157 try sema.errNote(inst_src, msg, "use @alignCast to assert pointer alignment", .{});
24158 break :msg msg;
24159 }),
24160 }
24161}
24162
24163fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte_subtract: u64, new_ty: Type) !Value {
24164 const pt = sema.pt;
24165 const zcu = pt.zcu;
24166 if (byte_subtract == 0) return pt.getCoerced(ptr_val, new_ty);
24167 const ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) {
24168 .undef => return sema.failWithUseOfUndef(block, src, null),
24169 .ptr => |ptr| ptr,
24170 else => unreachable,
24171 };
24172 if (ptr.byte_offset < byte_subtract) {
24173 return sema.failWithOwnedErrorMsg(block, msg: {
24174 const msg = try sema.errMsg(src, "pointer computation here causes illegal behavior", .{});
24175 errdefer msg.destroy(sema.gpa);
24176 try sema.errNote(src, msg, "resulting pointer exceeds bounds of containing value which may trigger overflow", .{});
24177 break :msg msg;
24178 });
24179 }
24180 return Value.fromInterned(try pt.intern(.{ .ptr = .{
24181 .ty = new_ty.toIntern(),
24182 .base_addr = ptr.base_addr,
24183 .byte_offset = ptr.byte_offset - byte_subtract,
24184 } }));
24185}
24186
24187fn zirMinMax(
24188 sema: *Sema,
24189 block: *Block,
24190 inst: Zir.Inst.Index,
24191 comptime air_tag: Air.Inst.Tag,
24192) CompileError!Air.Inst.Ref {
24193 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
24194 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
24195 const src = block.nodeOffset(inst_data.src_node);
24196 const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0);
24197 const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1);
24198 const lhs = sema.resolveInst(extra.lhs);
24199 const rhs = sema.resolveInst(extra.rhs);
24200 return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src });
24201}
24202
24203fn zirMinMaxMulti(
24204 sema: *Sema,
24205 block: *Block,
24206 extended: Zir.Inst.Extended.InstData,
24207 comptime air_tag: Air.Inst.Tag,
24208) CompileError!Air.Inst.Ref {
24209 const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
24210 const src_node = extra.data.src_node;
24211 const src = block.nodeOffset(src_node);
24212 const operands = sema.code.refSlice(extra.end, extended.small);
24213
24214 const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len);
24215 const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len);
24216
24217 for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| {
24218 op_src.* = block.builtinCallArgSrc(src_node, @intCast(i));
24219 air_ref.* = sema.resolveInst(zir_ref);
24220 }
24221
24222 return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs);
24223}
24224
24225fn analyzeMinMax(
24226 sema: *Sema,
24227 block: *Block,
24228 src: LazySrcLoc,
24229 comptime air_tag: Air.Inst.Tag,
24230 operands: []const Air.Inst.Ref,
24231 operand_srcs: []const LazySrcLoc,
24232) CompileError!Air.Inst.Ref {
24233 assert(operands.len == operand_srcs.len);
24234 assert(operands.len > 0);
24235
24236 const pt = sema.pt;
24237 const zcu = pt.zcu;
24238
24239 // This function has the signature `fn (Value, Value, *Zcu) Value`.
24240 // It is only used on scalar values, although the values may have different types.
24241 // If either operand is undef, it returns undef.
24242 const opFunc = switch (air_tag) {
24243 .min => Value.numberMin,
24244 .max => Value.numberMax,
24245 else => comptime unreachable,
24246 };
24247
24248 if (operands.len == 1) {
24249 try sema.checkNumericType(block, operand_srcs[0], sema.typeOf(operands[0]));
24250 return operands[0];
24251 }
24252
24253 // First, basic type validation; we'll make sure all the operands are numeric and agree on vector length.
24254 // This value will be `null` for a scalar type, otherwise the length of the vector type.
24255 const vector_len: ?u64 = vec_len: {
24256 const first_operand_ty = sema.typeOf(operands[0]);
24257 try sema.checkNumericType(block, operand_srcs[0], first_operand_ty);
24258 if (first_operand_ty.zigTypeTag(zcu) == .vector) {
24259 const vec_len = first_operand_ty.vectorLen(zcu);
24260 for (operands[1..], operand_srcs[1..]) |operand, operand_src| {
24261 const operand_ty = sema.typeOf(operand);
24262 try sema.checkNumericType(block, operand_src, operand_ty);
24263 if (operand_ty.zigTypeTag(zcu) != .vector) {
24264 return sema.failWithOwnedErrorMsg(block, msg: {
24265 const msg = try sema.errMsg(operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)});
24266 errdefer msg.destroy(zcu.gpa);
24267 try sema.errNote(operand_srcs[0], msg, "vector operand here", .{});
24268 break :msg msg;
24269 });
24270 }
24271 if (operand_ty.vectorLen(zcu) != vec_len) {
24272 return sema.failWithOwnedErrorMsg(block, msg: {
24273 const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{f}'", .{ vec_len, operand_ty.fmt(pt) });
24274 errdefer msg.destroy(zcu.gpa);
24275 try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len});
24276 break :msg msg;
24277 });
24278 }
24279 }
24280 break :vec_len vec_len;
24281 } else {
24282 for (operands[1..], operand_srcs[1..]) |operand, operand_src| {
24283 const operand_ty = sema.typeOf(operand);
24284 try sema.checkNumericType(block, operand_src, operand_ty);
24285 if (operand_ty.zigTypeTag(zcu) == .vector) {
24286 return sema.failWithOwnedErrorMsg(block, msg: {
24287 const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{f}'", .{first_operand_ty.fmt(pt)});
24288 errdefer msg.destroy(zcu.gpa);
24289 try sema.errNote(operand_src, msg, "vector operand here", .{});
24290 break :msg msg;
24291 });
24292 }
24293 }
24294 break :vec_len null;
24295 }
24296 };
24297
24298 // Now we want to look at the scalar types. If any is a float, our result will be a float. This
24299 // union is in "priority" order: `float` overrides `comptime_float` overrides `int`.
24300 const TypeStrat = union(enum) {
24301 float: Type,
24302 comptime_float,
24303 int: struct {
24304 /// If this is still `true` at the end, we will just use a `comptime_int`.
24305 all_comptime_int: bool,
24306 // These two fields tells us about the *result* type, which is refined based on operand types.
24307 // e.g. `@max(u32, i64)` results in a `u63`, because the result is >=0 and <=maxInt(i64).
24308 result_min: Value,
24309 result_max: Value,
24310 // These two fields tell us the *intermediate* type to use for actually computing the min/max.
24311 // e.g. `@max(u32, i64)` uses an intermediate `i64`, because it can fit all our operands.
24312 operand_min: Value,
24313 operand_max: Value,
24314 },
24315 none,
24316 };
24317 var cur_strat: TypeStrat = .none;
24318 for (operands) |operand| {
24319 const operand_scalar_ty = sema.typeOf(operand).scalarType(zcu);
24320 const want_strat: TypeStrat = switch (operand_scalar_ty.zigTypeTag(zcu)) {
24321 .comptime_int => s: {
24322 const val = sema.resolveValue(operand).?;
24323 if (val.isUndef(zcu)) break :s .none;
24324 break :s .{ .int = .{
24325 .all_comptime_int = true,
24326 .result_min = val,
24327 .result_max = val,
24328 .operand_min = val,
24329 .operand_max = val,
24330 } };
24331 },
24332 .comptime_float => .comptime_float,
24333 .float => .{ .float = operand_scalar_ty },
24334 .int => s: {
24335 // If the *value* is comptime-known, we will use that to get tighter bounds. If #3806
24336 // is accepted and implemented, so that integer literals have a tightly-bounded ranged
24337 // integer type (and `comptime_int` ceases to exist), this block should probably go away
24338 // (replaced with just the simple calls to `Type.minInt`/`Type.maxInt`) so that we only
24339 // use the input *types* to determine the result type.
24340 const min: Value, const max: Value = bounds: {
24341 if (sema.resolveValue(operand)) |operand_val| {
24342 if (vector_len) |len| {
24343 var min = try operand_val.elemValue(pt, 0);
24344 var max = min;
24345 for (1..@intCast(len)) |elem_idx| {
24346 const elem_val = try operand_val.elemValue(pt, elem_idx);
24347 min = Value.numberMin(min, elem_val, zcu);
24348 max = Value.numberMax(max, elem_val, zcu);
24349 }
24350 if (!min.isUndef(zcu) and !max.isUndef(zcu)) {
24351 break :bounds .{ min, max };
24352 }
24353 } else {
24354 if (!operand_val.isUndef(zcu)) {
24355 break :bounds .{ operand_val, operand_val };
24356 }
24357 }
24358 }
24359 break :bounds .{
24360 try operand_scalar_ty.minInt(pt, operand_scalar_ty),
24361 try operand_scalar_ty.maxInt(pt, operand_scalar_ty),
24362 };
24363 };
24364 break :s .{ .int = .{
24365 .all_comptime_int = false,
24366 .result_min = min,
24367 .result_max = max,
24368 .operand_min = min,
24369 .operand_max = max,
24370 } };
24371 },
24372 else => unreachable,
24373 };
24374 if (@backingInt(want_strat) < @backingInt(cur_strat)) {
24375 // `want_strat` overrides `cur_strat`.
24376 cur_strat = want_strat;
24377 } else if (@backingInt(want_strat) == @backingInt(cur_strat)) {
24378 // The behavior depends on the tag.
24379 switch (cur_strat) {
24380 .none, .comptime_float => {}, // no payload, so nop
24381 .float => |cur_float| {
24382 const want_float = want_strat.float;
24383 // Select the larger bit size. If the bit size is the same, select whichever is not c_longdouble.
24384 const cur_bits = cur_float.floatBits(zcu.getTarget());
24385 const want_bits = want_float.floatBits(zcu.getTarget());
24386 if (want_bits > cur_bits or
24387 (want_bits == cur_bits and
24388 cur_float.toIntern() == .c_longdouble_type and
24389 want_float.toIntern() != .c_longdouble_type))
24390 {
24391 cur_strat = want_strat;
24392 }
24393 },
24394 .int => |*cur_int| {
24395 const want_int = want_strat.int;
24396 if (!want_int.all_comptime_int) cur_int.all_comptime_int = false;
24397 cur_int.result_min = opFunc(cur_int.result_min, want_int.result_min, zcu);
24398 cur_int.result_max = opFunc(cur_int.result_max, want_int.result_max, zcu);
24399 cur_int.operand_min = Value.numberMin(cur_int.operand_min, want_int.operand_min, zcu);
24400 cur_int.operand_max = Value.numberMax(cur_int.operand_max, want_int.operand_max, zcu);
24401 },
24402 }
24403 }
24404 }
24405
24406 // Use `cur_strat` to actually resolve the result type (and intermediate type).
24407 const result_scalar_ty: Type, const intermediate_scalar_ty: Type = switch (cur_strat) {
24408 .float => |ty| .{ ty, ty },
24409 .comptime_float => .{ .comptime_float, .comptime_float },
24410 .int => |int| if (int.all_comptime_int) .{
24411 .comptime_int,
24412 .comptime_int,
24413 } else .{
24414 try pt.intFittingRange(int.result_min, int.result_max),
24415 try pt.intFittingRange(int.operand_min, int.operand_max),
24416 },
24417 .none => .{ .comptime_int, .comptime_int }, // all undef comptime ints
24418 };
24419 const result_ty: Type = if (vector_len) |l| try pt.vectorType(.{
24420 .len = @intCast(l),
24421 .child = result_scalar_ty.toIntern(),
24422 }) else result_scalar_ty;
24423 const intermediate_ty: Type = if (vector_len) |l| try pt.vectorType(.{
24424 .len = @intCast(l),
24425 .child = intermediate_scalar_ty.toIntern(),
24426 }) else intermediate_scalar_ty;
24427
24428 // We might have refined all the way down to an OPV type---check now.
24429 if (try result_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
24430
24431 // This value, if not `null`, will have type `intermediate_ty`.
24432 const comptime_part: ?Value = ct: {
24433 // Contains the comptime-known scalar result values.
24434 // Values are scalars with no particular type.
24435 // `elems.len` is `vector_len orelse 1`.
24436 const elems: []InternPool.Index = try sema.arena.alloc(
24437 InternPool.Index,
24438 try sema.usizeCast(block, src, vector_len orelse 1),
24439 );
24440 // If `false`, we've not seen any comptime-known operand yet, so `elems` contains `undefined`.
24441 // Otherwise, `elems` is populated with the comptime-known results so far.
24442 var elems_populated = false;
24443 // Populated when we see a runtime-known operand.
24444 var opt_runtime_src: ?LazySrcLoc = null;
24445
24446 for (operands, operand_srcs) |operand, operand_src| {
24447 const operand_val = sema.resolveValue(operand) orelse {
24448 if (opt_runtime_src == null) opt_runtime_src = operand_src;
24449 continue;
24450 };
24451 if (vector_len) |len| {
24452 // Vector case; apply `opFunc` to each element.
24453 if (elems_populated) {
24454 for (elems, 0..@intCast(len)) |*elem, elem_idx| {
24455 const new_elem = try operand_val.elemValue(pt, elem_idx);
24456 elem.* = opFunc(.fromInterned(elem.*), new_elem, zcu).toIntern();
24457 }
24458 } else {
24459 elems_populated = true;
24460 for (elems, 0..@intCast(len)) |*elem_out, elem_idx| {
24461 elem_out.* = (try operand_val.elemValue(pt, elem_idx)).toIntern();
24462 }
24463 }
24464 } else {
24465 // Scalar case; just apply `opFunc`.
24466 if (elems_populated) {
24467 elems[0] = opFunc(.fromInterned(elems[0]), operand_val, zcu).toIntern();
24468 } else {
24469 elems_populated = true;
24470 elems[0] = operand_val.toIntern();
24471 }
24472 }
24473 }
24474 const runtime_src = opt_runtime_src orelse {
24475 // The result is comptime-known. Coerce each element to its scalar type.
24476 assert(elems_populated);
24477 for (elems) |*elem| {
24478 if (Value.fromInterned(elem.*).isUndef(zcu)) {
24479 elem.* = (try pt.undefValue(result_scalar_ty)).toIntern();
24480 } else {
24481 // This coercion will always succeed, because `result_scalar_ty` can definitely hold the result.
24482 const coerced_ref = try sema.coerce(block, result_scalar_ty, Air.internedToRef(elem.*), .unneeded);
24483 elem.* = coerced_ref.toInterned().?;
24484 }
24485 }
24486 if (vector_len == null) return Air.internedToRef(elems[0]);
24487 return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern());
24488 };
24489 _ = runtime_src;
24490 // The result is runtime-known.
24491 // Coerce each element to the intermediate scalar type, unless there were no comptime-known operands.
24492 if (!elems_populated) break :ct null;
24493 for (elems) |*elem| {
24494 if (Value.fromInterned(elem.*).isUndef(zcu)) {
24495 elem.* = (try pt.undefValue(intermediate_scalar_ty)).toIntern();
24496 } else {
24497 // This coercion will always succeed, because `intermediate_scalar_ty` can definitely hold all operands.
24498 const coerced_ref = try sema.coerce(block, intermediate_scalar_ty, Air.internedToRef(elem.*), .unneeded);
24499 elem.* = coerced_ref.toInterned().?;
24500 }
24501 }
24502 break :ct if (vector_len != null)
24503 try pt.aggregateValue(intermediate_ty, elems)
24504 else
24505 .fromInterned(elems[0]);
24506 };
24507
24508 // 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.
24509
24510 // `.none` indicates no result so far.
24511 var cur_result: Air.Inst.Ref = if (comptime_part) |val| Air.internedToRef(val.toIntern()) else .none;
24512 for (operands, operand_srcs) |operand, operand_src| {
24513 if (try sema.isComptimeKnown(operand)) continue; // already in `comptime_part`
24514 // This coercion could fail; e.g. coercing a runtime integer peer to a `comptime_float` in a case like `@min(runtime_int, 1.5)`.
24515 const operand_coerced = try sema.coerce(block, intermediate_ty, operand, operand_src);
24516 if (cur_result == .none) {
24517 cur_result = operand_coerced;
24518 } else {
24519 cur_result = try block.addBinOp(air_tag, cur_result, operand_coerced);
24520 }
24521 }
24522
24523 assert(cur_result != .none);
24524 assert(sema.typeOf(cur_result).toIntern() == intermediate_ty.toIntern());
24525
24526 // 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.
24527 if (comptime_part) |val| {
24528 if (val.isUndef(zcu)) {
24529 return pt.undefRef(result_ty);
24530 }
24531 }
24532
24533 if (result_ty.toIntern() == intermediate_ty.toIntern()) {
24534 // No final cast needed; we're all done.
24535 return cur_result;
24536 }
24537
24538 // A final cast is needed. The only case where `intermediate_ty` is different is for integers,
24539 // where we have refined the range, so we should be doing an intcast.
24540 assert(intermediate_scalar_ty.zigTypeTag(zcu) == .int);
24541 assert(result_scalar_ty.zigTypeTag(zcu) == .int);
24542 return block.addTyOp(.int_cast, result_ty, cur_result);
24543}
24544
24545fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref {
24546 const pt = sema.pt;
24547 const zcu = pt.zcu;
24548 const ptr_ty = sema.typeOf(ptr);
24549 const info = ptr_ty.ptrInfo(zcu);
24550 if (info.flags.size == .one) {
24551 // Already an array pointer.
24552 return ptr;
24553 }
24554 const new_ty = try pt.ptrType(.{
24555 .child = (try pt.arrayType(.{
24556 .len = len,
24557 .sentinel = info.sentinel,
24558 .child = info.child,
24559 })).toIntern(),
24560 .flags = .{
24561 .alignment = info.flags.alignment,
24562 .is_const = info.flags.is_const,
24563 .is_volatile = info.flags.is_volatile,
24564 .is_allowzero = info.flags.is_allowzero,
24565 .address_space = info.flags.address_space,
24566 },
24567 });
24568 const non_slice_ptr = if (info.flags.size == .slice)
24569 try block.addTyOp(.slice_ptr, ptr_ty.slicePtrFieldType(zcu), ptr)
24570 else
24571 ptr;
24572 return block.addTyOp(.ptr_cast, new_ty, non_slice_ptr);
24573}
24574
24575fn zirMemcpy(
24576 sema: *Sema,
24577 block: *Block,
24578 inst: Zir.Inst.Index,
24579 air_tag: Air.Inst.Tag,
24580 check_aliasing: bool,
24581) CompileError!void {
24582 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
24583 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
24584 const src = block.nodeOffset(inst_data.src_node);
24585 const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0);
24586 const src_src = block.builtinCallArgSrc(inst_data.src_node, 1);
24587 const dest_ptr = sema.resolveInst(extra.lhs);
24588 const src_ptr = sema.resolveInst(extra.rhs);
24589 const dest_ty = sema.typeOf(dest_ptr);
24590 const src_ty = sema.typeOf(src_ptr);
24591 const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr);
24592 const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr);
24593 const pt = sema.pt;
24594 const zcu = pt.zcu;
24595
24596 if (dest_ty.isConstPtr(zcu)) {
24597 return sema.fail(block, dest_src, "cannot copy to constant pointer", .{});
24598 }
24599
24600 if (dest_len == .none and src_len == .none) {
24601 const msg = msg: {
24602 const msg = try sema.errMsg(src, "unknown copy length", .{});
24603 errdefer msg.destroy(sema.gpa);
24604 try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{
24605 dest_ty.fmt(pt),
24606 });
24607 try sema.errNote(src_src, msg, "source type '{f}' provides no length", .{
24608 src_ty.fmt(pt),
24609 });
24610 break :msg msg;
24611 };
24612 return sema.failWithOwnedErrorMsg(block, msg);
24613 }
24614
24615 const dest_elem_ty = dest_ty.indexableElem(zcu);
24616 const src_elem_ty = src_ty.indexableElem(zcu);
24617
24618 try sema.ensureLayoutResolved(dest_elem_ty, dest_src, .ptr_access);
24619 try sema.ensureLayoutResolved(src_elem_ty, src_src, .ptr_access);
24620
24621 const imc = try sema.coerceInMemoryAllowed(
24622 block,
24623 dest_elem_ty,
24624 src_elem_ty,
24625 false,
24626 zcu.getTarget(),
24627 dest_src,
24628 src_src,
24629 null,
24630 );
24631 if (imc != .ok) return sema.failWithOwnedErrorMsg(block, msg: {
24632 const msg = try sema.errMsg(
24633 src,
24634 "pointer element type '{f}' cannot coerce into element type '{f}'",
24635 .{ src_elem_ty.fmt(pt), dest_elem_ty.fmt(pt) },
24636 );
24637 errdefer msg.destroy(sema.gpa);
24638 try imc.report(sema, src, msg);
24639 break :msg msg;
24640 });
24641
24642 var len_val: ?Value = null;
24643
24644 if (dest_len != .none and src_len != .none) check: {
24645 // If we can check at compile-time, no need for runtime safety.
24646 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
24647 len_val = dest_len_val;
24648 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
24649 if (!(try sema.valuesEqual(dest_len_val, src_len_val, .usize))) {
24650 const msg = msg: {
24651 const msg = try sema.errMsg(src, "non-matching copy lengths", .{});
24652 errdefer msg.destroy(sema.gpa);
24653 try sema.errNote(dest_src, msg, "length {f} here", .{
24654 dest_len_val.fmtValueSema(pt, sema),
24655 });
24656 try sema.errNote(src_src, msg, "length {f} here", .{
24657 src_len_val.fmtValueSema(pt, sema),
24658 });
24659 break :msg msg;
24660 };
24661 return sema.failWithOwnedErrorMsg(block, msg);
24662 }
24663 break :check;
24664 }
24665 } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
24666 len_val = src_len_val;
24667 }
24668
24669 if (block.wantSafety()) {
24670 const ok = try block.addBinOp(.cmp_eq, dest_len, src_len);
24671 try sema.addSafetyCheck(block, src, ok, .copy_len_mismatch);
24672 }
24673 } else if (dest_len != .none) {
24674 if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| {
24675 len_val = dest_len_val;
24676 }
24677 } else if (src_len != .none) {
24678 if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| {
24679 len_val = src_len_val;
24680 }
24681 }
24682
24683 zero_bit: {
24684 const src_comptime = src_elem_ty.comptimeOnly(zcu);
24685 const dest_comptime = dest_elem_ty.comptimeOnly(zcu);
24686 assert(src_comptime == dest_comptime); // IMC
24687 if (src_comptime) break :zero_bit;
24688
24689 const src_has_bits = src_elem_ty.hasRuntimeBits(zcu);
24690 const dest_has_bits = dest_elem_ty.hasRuntimeBits(zcu);
24691 assert(src_has_bits == dest_has_bits); // IMC
24692 if (src_has_bits) break :zero_bit;
24693
24694 // The element type is zero-bit. We've done all validation (aside from the aliasing check,
24695 // which we must skip) so we're done.
24696 return;
24697 }
24698
24699 const runtime_src = rs: {
24700 const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src;
24701 const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src;
24702
24703 const raw_dest_ptr = if (dest_ty.isSlice(zcu)) dest_ptr_val.slicePtr(zcu) else dest_ptr_val;
24704 const raw_src_ptr = if (src_ty.isSlice(zcu)) src_ptr_val.slicePtr(zcu) else src_ptr_val;
24705
24706 const len_u64 = len_val.?.toUnsignedInt(zcu);
24707
24708 if (check_aliasing) {
24709 if (Value.doPointersOverlap(
24710 raw_src_ptr,
24711 raw_dest_ptr,
24712 len_u64,
24713 zcu,
24714 )) return sema.fail(block, src, "'@memcpy' arguments alias", .{});
24715 }
24716
24717 if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src;
24718
24719 // Because comptime pointer access is a somewhat expensive operation, we implement @memcpy
24720 // as one load and store of an array, rather than N loads and stores of individual elements.
24721
24722 const array_ty = try pt.arrayType(.{
24723 .child = dest_elem_ty.toIntern(),
24724 .len = len_u64,
24725 });
24726
24727 const dest_array_ptr_ty = try pt.ptrType(info: {
24728 var info = dest_ty.ptrInfo(zcu);
24729 info.flags.size = .one;
24730 info.child = array_ty.toIntern();
24731 info.sentinel = .none;
24732 break :info info;
24733 });
24734 const src_array_ptr_ty = try pt.ptrType(info: {
24735 var info = src_ty.ptrInfo(zcu);
24736 info.flags.size = .one;
24737 info.child = array_ty.toIntern();
24738 info.sentinel = .none;
24739 break :info info;
24740 });
24741
24742 const coerced_dest_ptr = try pt.getCoerced(raw_dest_ptr, dest_array_ptr_ty);
24743 const coerced_src_ptr = try pt.getCoerced(raw_src_ptr, src_array_ptr_ty);
24744
24745 const array_val = try sema.pointerDeref(block, src_src, coerced_src_ptr, src_array_ptr_ty) orelse break :rs src_src;
24746 try sema.storePtrVal(block, dest_src, coerced_dest_ptr, array_val, array_ty);
24747 return;
24748 };
24749
24750 // If the length is comptime-known, then upgrade src and destination types
24751 // into pointer-to-array. At this point we know they are both pointers
24752 // already.
24753 var new_dest_ptr = dest_ptr;
24754 var new_src_ptr = src_ptr;
24755 if (len_val) |val| {
24756 const len = val.toUnsignedInt(zcu);
24757 if (len == 0) {
24758 // This AIR instruction guarantees length > 0 if it is comptime-known.
24759 return;
24760 }
24761 new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len);
24762 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
24763 }
24764
24765 if (dest_len != .none) {
24766 // Change the src from slice to a many pointer, to avoid multiple ptr
24767 // slice extractions in AIR instructions.
24768 const new_src_ptr_ty = sema.typeOf(new_src_ptr);
24769 if (new_src_ptr_ty.isSlice(zcu)) {
24770 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
24771 }
24772 } else if (dest_len == .none and len_val == null) {
24773 // Change the dest to a slice, since its type must have the length.
24774 const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr, .none);
24775 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);
24776 const new_src_ptr_ty = sema.typeOf(new_src_ptr);
24777 if (new_src_ptr_ty.isSlice(zcu)) {
24778 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
24779 }
24780 }
24781
24782 try sema.requireRuntimeBlock(block, src, runtime_src);
24783 try sema.validateRuntimeValue(block, dest_src, dest_ptr);
24784 try sema.validateRuntimeValue(block, src_src, src_ptr);
24785
24786 // Aliasing safety check.
24787 if (check_aliasing and block.wantSafety()) {
24788 const len = if (len_val) |v|
24789 Air.internedToRef(v.toIntern())
24790 else if (dest_len != .none)
24791 dest_len
24792 else
24793 src_len;
24794
24795 // Extract raw pointer from dest slice. The AIR instructions could support them, but
24796 // it would cause redundant machine code instructions.
24797 const new_dest_ptr_ty = sema.typeOf(new_dest_ptr);
24798 const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(zcu))
24799 try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty)
24800 else if (new_dest_ptr_ty.ptrSize(zcu) == .one) ptr: {
24801 var dest_manyptr_ty_key = zcu.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type;
24802 assert(dest_manyptr_ty_key.flags.size == .one);
24803 dest_manyptr_ty_key.child = dest_elem_ty.toIntern();
24804 dest_manyptr_ty_key.flags.size = .many;
24805 break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src);
24806 } else new_dest_ptr;
24807
24808 const new_src_ptr_ty = sema.typeOf(new_src_ptr);
24809 const raw_src_ptr = if (new_src_ptr_ty.isSlice(zcu))
24810 try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty)
24811 else if (new_src_ptr_ty.ptrSize(zcu) == .one) ptr: {
24812 var src_manyptr_ty_key = zcu.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type;
24813 assert(src_manyptr_ty_key.flags.size == .one);
24814 src_manyptr_ty_key.child = src_elem_ty.toIntern();
24815 src_manyptr_ty_key.flags.size = .many;
24816 break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(src_manyptr_ty_key), new_src_ptr, src_src);
24817 } else new_src_ptr;
24818
24819 // ok1: dest >= src + len
24820 // ok2: src >= dest + len
24821 const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src);
24822 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, src);
24823 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
24824 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
24825 const ok = try block.addBinOp(.bit_or, ok1, ok2);
24826 try sema.addSafetyCheck(block, src, ok, .memcpy_alias);
24827 }
24828
24829 _ = try block.addInst(.{
24830 .tag = air_tag,
24831 .data = .{ .bin_op = .{
24832 .lhs = new_dest_ptr,
24833 .rhs = new_src_ptr,
24834 } },
24835 });
24836}
24837
24838fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
24839 const pt = sema.pt;
24840 const zcu = pt.zcu;
24841 const comp = zcu.comp;
24842 const gpa = comp.gpa;
24843 const io = comp.io;
24844 const ip = &zcu.intern_pool;
24845
24846 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
24847 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
24848 const src = block.nodeOffset(inst_data.src_node);
24849 const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0);
24850 const value_src = block.builtinCallArgSrc(inst_data.src_node, 1);
24851 const dest_ptr = sema.resolveInst(extra.lhs);
24852 const uncoerced_elem = sema.resolveInst(extra.rhs);
24853 const dest_ptr_ty = sema.typeOf(dest_ptr);
24854 try checkMemOperand(sema, block, dest_src, dest_ptr_ty);
24855
24856 if (dest_ptr_ty.isConstPtr(zcu)) {
24857 return sema.fail(block, dest_src, "cannot memset constant pointer", .{});
24858 }
24859
24860 const dest_elem_ty: Type = dest_elem_ty: {
24861 const ptr_info = dest_ptr_ty.ptrInfo(zcu);
24862 switch (ptr_info.flags.size) {
24863 .slice => break :dest_elem_ty .fromInterned(ptr_info.child),
24864 .one => {
24865 if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) {
24866 break :dest_elem_ty Type.fromInterned(ptr_info.child).childType(zcu);
24867 }
24868 },
24869 .many, .c => {},
24870 }
24871 return sema.failWithOwnedErrorMsg(block, msg: {
24872 const msg = try sema.errMsg(src, "unknown @memset length", .{});
24873 errdefer msg.destroy(sema.gpa);
24874 try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{
24875 dest_ptr_ty.fmt(pt),
24876 });
24877 break :msg msg;
24878 });
24879 };
24880
24881 const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src);
24882
24883 const comptime_only_elem = switch (dest_elem_ty.classify(zcu)) {
24884 .no_possible_value => unreachable, // `elem` is a value of this type
24885 .one_possible_value => return, // no work to do
24886 .runtime => false,
24887 .partially_comptime, .fully_comptime => true,
24888 };
24889
24890 const runtime_src = rs: {
24891 const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), dest_src);
24892 const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src;
24893 const len_u64 = len_val.toUnsignedInt(zcu);
24894 const len = try sema.usizeCast(block, dest_src, len_u64);
24895 if (len == 0) {
24896 // This AIR instruction guarantees length > 0 if it is comptime-known.
24897 return;
24898 }
24899
24900 const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src;
24901 if (!sema.isComptimeMutablePtr(ptr_val)) break :rs dest_src;
24902 const elem_val = sema.resolveValue(elem) orelse break :rs value_src;
24903 const array_ty = try pt.arrayType(.{
24904 .child = dest_elem_ty.toIntern(),
24905 .len = len_u64,
24906 });
24907 const array_val = try pt.aggregateSplatValue(array_ty, elem_val);
24908 const array_ptr_ty = ty: {
24909 var info = dest_ptr_ty.ptrInfo(zcu);
24910 info.flags.size = .one;
24911 info.child = array_ty.toIntern();
24912 break :ty try pt.ptrType(info);
24913 };
24914 const raw_ptr_val = if (dest_ptr_ty.isSlice(zcu)) ptr_val.slicePtr(zcu) else ptr_val;
24915 const array_ptr_val = try pt.getCoerced(raw_ptr_val, array_ptr_ty);
24916 return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty);
24917 };
24918
24919 if (comptime_only_elem) {
24920 return sema.failWithOwnedErrorMsg(block, msg: {
24921 const msg = try sema.errMsg(src, "cannot store comptime-only element '{f}' at runtime", .{dest_elem_ty.fmt(pt)});
24922 errdefer msg.destroy(sema.gpa);
24923 try sema.errNote(dest_src, msg, "operation is runtime due to destination pointer", .{});
24924 break :msg msg;
24925 });
24926 }
24927
24928 try sema.requireRuntimeBlock(block, src, runtime_src);
24929 try sema.validateRuntimeValue(block, dest_src, dest_ptr);
24930 try sema.validateRuntimeValue(block, value_src, elem);
24931
24932 _ = try block.addInst(.{
24933 .tag = if (block.wantSafety()) .memset_safe else .memset,
24934 .data = .{ .bin_op = .{
24935 .lhs = dest_ptr,
24936 .rhs = elem,
24937 } },
24938 });
24939}
24940
24941fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
24942 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node;
24943 const src = block.nodeOffset(inst_data.src_node);
24944 return sema.failWithUseOfAsync(block, src);
24945}
24946
24947fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
24948 const pt = sema.pt;
24949 const zcu = pt.zcu;
24950 const comp = zcu.comp;
24951 const gpa = comp.gpa;
24952 const io = comp.io;
24953 const ip = &zcu.intern_pool;
24954
24955 const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node;
24956 const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
24957 const target = zcu.getTarget();
24958
24959 const cc_src = block.src(.{ .node_offset_fn_type_cc = inst_data.src_node });
24960 const ret_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node });
24961 const has_body = extra.data.body_len != 0;
24962
24963 var extra_index: usize = extra.end;
24964
24965 const cc: std.lang.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
24966 const body_len = sema.code.extra[extra_index];
24967 extra_index += 1;
24968 const body = sema.code.bodySlice(extra_index, body_len);
24969 extra_index += body.len;
24970
24971 const cc_ty = try sema.getStdLangType(cc_src, .CallingConvention);
24972 const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, .{ .simple = .@"callconv" });
24973 break :blk try sema.analyzeValueAsCallconv(block, cc_src, val);
24974 } else if (extra.data.bits.has_cc_ref) blk: {
24975 const cc_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index]));
24976 extra_index += 1;
24977 const cc_ty = try sema.getStdLangType(cc_src, .CallingConvention);
24978 const uncoerced_cc = sema.resolveInst(cc_ref);
24979 const coerced_cc = try sema.coerce(block, cc_ty, uncoerced_cc, cc_src);
24980 const cc_val = try sema.resolveConstDefinedValue(block, cc_src, coerced_cc, .{ .simple = .@"callconv" });
24981 break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val);
24982 } else cc: {
24983 if (has_body) {
24984 const func_decl_nav = sema.owner.unwrap().nav_val;
24985 const func_decl_ti = ip.getNav(func_decl_nav).analysis.?.zir_index;
24986 const func_decl_inst = func_decl_ti.resolve(&zcu.intern_pool) orelse {
24987 return sema.failTransitive(.{ .lost_tracking = func_decl_ti });
24988 };
24989 const zir_decl = sema.code.getDeclaration(func_decl_inst);
24990 if (zir_decl.linkage == .@"export") {
24991 break :cc target.cCallingConvention() orelse {
24992 // This target has no default C calling convention. We sometimes trigger a similar
24993 // error by trying to evaluate `std.lang.CallingConvention.c`, so for consistency,
24994 // let's eval that now and just get the transitive error. (It's guaranteed to error
24995 // because it does the exact `cCallingConvention` call we just did.)
24996 const cc_type = try sema.getStdLangType(cc_src, .CallingConvention);
24997 _ = try sema.namespaceLookupVal(
24998 block,
24999 LazySrcLoc.unneeded,
25000 cc_type.getNamespaceIndex(zcu),
25001 try ip.getOrPutString(gpa, io, pt.tid, "c", .no_embedded_nulls),
25002 );
25003 // The above should have errored.
25004 @panic("std.lang is corrupt");
25005 };
25006 }
25007 }
25008 break :cc .auto;
25009 };
25010
25011 const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
25012 const body_len = sema.code.extra[extra_index];
25013 extra_index += 1;
25014 const body = sema.code.bodySlice(extra_index, body_len);
25015 extra_index += body.len;
25016 if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison;
25017
25018 const val = try sema.resolveGenericBody(block, ret_src, body, inst, .type, .{ .simple = .fn_ret_ty });
25019 const ty = val.toType();
25020 break :blk ty;
25021 } else if (extra.data.bits.has_ret_ty_ref) blk: {
25022 const ret_ty_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index]));
25023 extra_index += 1;
25024 if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison;
25025
25026 break :blk try sema.resolveType(block, ret_src, ret_ty_ref);
25027 } else .void;
25028
25029 const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: {
25030 const x = sema.code.extra[extra_index];
25031 extra_index += 1;
25032 break :blk x;
25033 } else 0;
25034
25035 var src_locs: Zir.Inst.Func.SrcLocs = undefined;
25036 if (has_body) {
25037 extra_index += extra.data.body_len;
25038 src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data;
25039 }
25040
25041 const is_var_args = extra.data.bits.is_var_args;
25042 const is_inferred_error = extra.data.bits.is_inferred_error;
25043 const is_noinline = extra.data.bits.is_noinline;
25044
25045 return sema.funcCommon(
25046 block,
25047 inst_data.src_node,
25048 inst,
25049 cc,
25050 ret_ty,
25051 is_var_args,
25052 is_inferred_error,
25053 has_body,
25054 src_locs,
25055 noalias_bits,
25056 is_noinline,
25057 );
25058}
25059
25060fn zirWasmMemorySize(
25061 sema: *Sema,
25062 block: *Block,
25063 extended: Zir.Inst.Extended.InstData,
25064) CompileError!Air.Inst.Ref {
25065 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
25066 const index_src = block.builtinCallArgSrc(extra.node, 0);
25067 const builtin_src = block.nodeOffset(extra.node);
25068 const target = sema.pt.zcu.getTarget();
25069 if (!target.cpu.arch.isWasm()) {
25070 return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
25071 }
25072
25073 const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.operand, .u32, .{ .simple = .wasm_memory_index }));
25074 try sema.requireRuntimeBlock(block, builtin_src, null);
25075 return block.addInst(.{
25076 .tag = .wasm_memory_size,
25077 .data = .{ .pl_op = .{
25078 .operand = .none,
25079 .payload = index,
25080 } },
25081 });
25082}
25083
25084fn zirWasmMemoryGrow(
25085 sema: *Sema,
25086 block: *Block,
25087 extended: Zir.Inst.Extended.InstData,
25088) CompileError!Air.Inst.Ref {
25089 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
25090 const builtin_src = block.nodeOffset(extra.node);
25091 const index_src = block.builtinCallArgSrc(extra.node, 0);
25092 const delta_src = block.builtinCallArgSrc(extra.node, 1);
25093 const target = sema.pt.zcu.getTarget();
25094 if (!target.cpu.arch.isWasm()) {
25095 return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)});
25096 }
25097
25098 const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.lhs, .u32, .{ .simple = .wasm_memory_index }));
25099 const delta = try sema.coerce(block, .usize, sema.resolveInst(extra.rhs), delta_src);
25100
25101 try sema.requireRuntimeBlock(block, builtin_src, null);
25102 return block.addInst(.{
25103 .tag = .wasm_memory_grow,
25104 .data = .{ .pl_op = .{
25105 .operand = delta,
25106 .payload = index,
25107 } },
25108 });
25109}
25110
25111fn resolvePrefetchOptions(
25112 sema: *Sema,
25113 block: *Block,
25114 src: LazySrcLoc,
25115 zir_ref: Zir.Inst.Ref,
25116) CompileError!std.lang.PrefetchOptions {
25117 const pt = sema.pt;
25118 const zcu = pt.zcu;
25119 const comp = zcu.comp;
25120 const gpa = comp.gpa;
25121 const io = comp.io;
25122 const ip = &zcu.intern_pool;
25123
25124 const options_ty = try sema.getStdLangType(src, .PrefetchOptions);
25125 const options = try sema.coerce(block, options_ty, sema.resolveInst(zir_ref), src);
25126
25127 const rw_src = block.src(.{ .init_field_rw = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25128 const locality_src = block.src(.{ .init_field_locality = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25129 const cache_src = block.src(.{ .init_field_cache = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25130
25131 const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "rw", .no_embedded_nulls), rw_src);
25132 const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{ .simple = .prefetch_options });
25133
25134 const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "locality", .no_embedded_nulls), locality_src);
25135 const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{ .simple = .prefetch_options });
25136
25137 const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "cache", .no_embedded_nulls), cache_src);
25138 const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{ .simple = .prefetch_options });
25139
25140 return std.lang.PrefetchOptions{
25141 .rw = try sema.interpretStdLangType(block, rw_src, rw_val, std.lang.PrefetchOptions.Rw),
25142 .locality = @intCast(locality_val.toUnsignedInt(zcu)),
25143 .cache = try sema.interpretStdLangType(block, cache_src, cache_val, std.lang.PrefetchOptions.Cache),
25144 };
25145}
25146
25147fn zirPrefetch(
25148 sema: *Sema,
25149 block: *Block,
25150 extended: Zir.Inst.Extended.InstData,
25151) CompileError!Air.Inst.Ref {
25152 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
25153 const ptr_src = block.builtinCallArgSrc(extra.node, 0);
25154 const opts_src = block.builtinCallArgSrc(extra.node, 1);
25155 const ptr = sema.resolveInst(extra.lhs);
25156 try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr));
25157
25158 const options = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs);
25159
25160 if (!block.isComptime()) {
25161 _ = try block.addInst(.{
25162 .tag = .prefetch,
25163 .data = .{ .prefetch = .{
25164 .ptr = ptr,
25165 .rw = options.rw,
25166 .locality = options.locality,
25167 .cache = options.cache,
25168 } },
25169 });
25170 }
25171
25172 return .void_value;
25173}
25174
25175fn resolveExternOptions(
25176 sema: *Sema,
25177 block: *Block,
25178 src: LazySrcLoc,
25179 zir_ref: Zir.Inst.Ref,
25180) CompileError!struct {
25181 name: InternPool.NullTerminatedString,
25182 library_name: InternPool.OptionalNullTerminatedString,
25183 linkage: std.lang.GlobalLinkage,
25184 visibility: std.lang.SymbolVisibility,
25185 is_thread_local: bool,
25186 is_dll_import: bool,
25187 relocation: std.lang.ExternOptions.Relocation,
25188 decoration: ?std.lang.ExternOptions.Decoration,
25189} {
25190 const pt = sema.pt;
25191 const zcu = pt.zcu;
25192 const comp = zcu.comp;
25193 const gpa = comp.gpa;
25194 const io = comp.io;
25195 const ip = &zcu.intern_pool;
25196
25197 const options_inst = sema.resolveInst(zir_ref);
25198 const extern_options_ty = try sema.getStdLangType(src, .ExternOptions);
25199 const options = try sema.coerce(block, extern_options_ty, options_inst, src);
25200
25201 const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25202 const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25203 const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25204 const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25205 const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25206 const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25207 const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25208 const decoration_src = block.src(.{ .init_field_decoration = src.offset.node_offset_builtin_call_arg.builtin_call_node });
25209
25210 const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "name", .no_embedded_nulls), name_src);
25211 const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });
25212
25213 const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "library_name", .no_embedded_nulls), library_src);
25214 const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ .simple = .extern_options });
25215
25216 const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "linkage", .no_embedded_nulls), linkage_src);
25217 const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options });
25218 const linkage = try sema.interpretStdLangType(block, linkage_src, linkage_val, std.lang.GlobalLinkage);
25219
25220 const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
25221 const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_ref, .{ .simple = .extern_options });
25222 const visibility = try sema.interpretStdLangType(block, visibility_src, visibility_val, std.lang.SymbolVisibility);
25223
25224 const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src);
25225 const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options });
25226
25227 const library_name = if (library_name_val.optionalValue(zcu)) |library_name_payload| library_name: {
25228 const library_name = try sema.toConstString(block, library_src, Air.internedToRef(library_name_payload.toIntern()), .{ .simple = .extern_options });
25229 if (library_name.len == 0) {
25230 return sema.fail(block, library_src, "library name cannot be empty", .{});
25231 }
25232 try sema.handleExternLibName(block, library_src, library_name);
25233 break :library_name library_name;
25234 } else null;
25235
25236 const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
25237 const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options });
25238
25239 const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "relocation", .no_embedded_nulls), relocation_src);
25240 const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });
25241 const relocation = try sema.interpretStdLangType(block, relocation_src, relocation_val, std.lang.ExternOptions.Relocation);
25242
25243 const decoration_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "decoration", .no_embedded_nulls), decoration_src);
25244 const decoration_val = try sema.resolveConstDefinedValue(block, decoration_src, decoration_ref, .{ .simple = .extern_options });
25245 const decoration = try sema.interpretStdLangType(block, decoration_src, decoration_val, ?std.lang.ExternOptions.Decoration);
25246
25247 if (name.len == 0) {
25248 return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
25249 }
25250
25251 if (linkage != .weak and linkage != .strong) {
25252 return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{});
25253 }
25254
25255 return .{
25256 .name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls),
25257 .library_name = try ip.getOrPutStringOpt(gpa, io, pt.tid, library_name, .no_embedded_nulls),
25258 .linkage = linkage,
25259 .visibility = visibility,
25260 .is_thread_local = is_thread_local_val.toBool(),
25261 .is_dll_import = is_dll_import_val.toBool(),
25262 .relocation = relocation,
25263 .decoration = decoration,
25264 };
25265}
25266
25267fn zirBuiltinExtern(
25268 sema: *Sema,
25269 block: *Block,
25270 extended: Zir.Inst.Extended.InstData,
25271) CompileError!Air.Inst.Ref {
25272 const pt = sema.pt;
25273 const zcu = pt.zcu;
25274 const ip = &zcu.intern_pool;
25275 const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
25276 const src = block.nodeOffset(extra.node);
25277 const ty_src = block.builtinCallArgSrc(extra.node, 0);
25278 const options_src = block.builtinCallArgSrc(extra.node, 1);
25279
25280 const ptr_ty = try sema.resolveType(block, ty_src, extra.lhs);
25281 if (!ptr_ty.isPtrAtRuntime(zcu)) {
25282 return sema.fail(block, ty_src, "expected (optional) pointer", .{});
25283 }
25284
25285 const ptr_info = ptr_ty.ptrInfo(zcu);
25286
25287 const elem_ty: Type = .fromInterned(ptr_info.child);
25288 try sema.ensureLayoutResolved(elem_ty, src, .@"extern");
25289
25290 if (!elem_ty.validateExtern(.other, zcu)) {
25291 return sema.failWithOwnedErrorMsg(block, msg: {
25292 const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ptr_ty.fmt(pt)});
25293 errdefer msg.destroy(sema.gpa);
25294 try sema.errNote(ty_src, msg, "pointer element type '{f}' is not extern compatible", .{elem_ty.fmt(pt)});
25295 try sema.explainWhyTypeIsNotExtern(msg, ty_src, elem_ty, .other);
25296 break :msg msg;
25297 });
25298 }
25299 if (elem_ty.zigTypeTag(zcu) == .@"fn" and !ptr_info.flags.is_const) {
25300 return sema.failWithOwnedErrorMsg(block, msg: {
25301 const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ptr_ty.fmt(pt)});
25302 errdefer msg.destroy(sema.gpa);
25303 try sema.errNote(ty_src, msg, "pointer to extern function must be 'const'", .{});
25304 break :msg msg;
25305 });
25306 }
25307
25308 const options = try sema.resolveExternOptions(block, options_src, extra.rhs);
25309 switch (options.linkage) {
25310 .internal => if (options.visibility != .default) {
25311 return sema.fail(block, options_src, "internal symbol cannot have non-default visibility", .{});
25312 },
25313 .strong, .weak => {},
25314 .link_once => return sema.fail(block, options_src, "external symbol cannot have link once linkage", .{}),
25315 }
25316 switch (options.relocation) {
25317 .any => {},
25318 .pcrel => if (options.visibility == .default) return sema.fail(block, options_src, "cannot require a pc-relative relocation to a symbol with default visibility", .{}),
25319 }
25320
25321 if (options.decoration) |decoration| switch (decoration) {
25322 .flat => switch (ptr_info.flags.address_space) {
25323 .input, .output => {},
25324 else => return sema.fail(block, options_src, "\"flat\" decoration requires \"input\" or \"output\" address space", .{}),
25325 },
25326 .location, .descriptor => {},
25327 };
25328
25329 const target = zcu.getTarget();
25330 switch (target.os.tag) {
25331 .vulkan, .opengl => {
25332 const pointee = switch (elem_ty.zigTypeTag(zcu)) {
25333 .array => elem_ty.childType(zcu),
25334 .spirv => if (elem_ty.isSpirvRuntimeArray(zcu)) elem_ty.childType(zcu) else elem_ty,
25335 else => elem_ty,
25336 };
25337 switch (ptr_info.flags.address_space) {
25338 .uniform,
25339 .storage_buffer,
25340 => if (ptr_info.flags.size != .one or pointee.zigTypeTag(zcu) != .@"struct") {
25341 return sema.fail(block, ty_src, "extern in '{t}' address space must be a single-item pointer to a struct", .{ptr_info.flags.address_space});
25342 },
25343 .push_constant => if (ptr_info.flags.size != .one or elem_ty.zigTypeTag(zcu) != .@"struct") {
25344 return sema.fail(block, ty_src, "extern in 'push_constant' address space must be a single-item pointer to a struct", .{});
25345 },
25346 .constant => if (target.os.tag == .vulkan and (pointee.zigTypeTag(zcu) != .spirv or pointee.isSpirvRuntimeArray(zcu))) {
25347 return sema.fail(block, ty_src, "extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one", .{});
25348 },
25349 else => if (elem_ty.isSpirvRuntimeArray(zcu)) {
25350 return sema.fail(block, ty_src, "SPIR-V runtime array is not allowed in the '{t}' address space", .{ptr_info.flags.address_space});
25351 },
25352 }
25353 },
25354 else => {},
25355 }
25356
25357 // TODO: error for threadlocal functions, non-const functions, etc
25358
25359 const extern_val = try pt.getExtern(.{
25360 .name = options.name,
25361 .ty = elem_ty.toIntern(),
25362 .lib_name = options.library_name,
25363 .linkage = options.linkage,
25364 .visibility = options.visibility,
25365 .is_threadlocal = options.is_thread_local,
25366 .is_dll_import = options.is_dll_import,
25367 .relocation = options.relocation,
25368 .decoration = options.decoration,
25369 .is_const = ptr_info.flags.is_const,
25370 .alignment = ptr_info.flags.alignment,
25371 .@"addrspace" = ptr_info.flags.address_space,
25372 // This instruction is just for source locations.
25373 // `builtin_extern` doesn't provide enough information, and isn't currently tracked.
25374 // So, for now, just use our containing `declaration`.
25375 .zir_index = switch (sema.owner.unwrap()) {
25376 .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,
25377 .type_layout, .struct_defaults => |owner_ty| Type.fromInterned(owner_ty).typeDeclInstAllowGeneratedTag(zcu).?,
25378 .memoized_state => unreachable,
25379 .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
25380 .func => |func| zir_index: {
25381 const func_info = zcu.funcInfo(func);
25382 const owner_func_info = if (func_info.generic_owner != .none) owner: {
25383 break :owner zcu.funcInfo(func_info.generic_owner);
25384 } else func_info;
25385 break :zir_index ip.getNav(owner_func_info.owner_nav).analysis.?.zir_index;
25386 },
25387 },
25388 .owner_nav = undefined, // ignored by `getExtern`
25389 .source = .builtin,
25390 });
25391
25392 // For a weak symbol where the given type is not nullable, make the pointer optional.
25393 const result_ptr_ty: Type = if (options.linkage == .weak and !ptr_ty.ptrAllowsZero(zcu)) ty: {
25394 break :ty try pt.optionalType(ptr_ty.toIntern());
25395 } else ptr_ty;
25396
25397 const uncasted_ptr = try sema.analyzeNavRef(block, src, ip.indexToKey(extern_val).@"extern".owner_nav);
25398 if (sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| {
25399 const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, result_ptr_ty);
25400 return Air.internedToRef(casted_ptr_val.toIntern());
25401 } else {
25402 return block.addTyOp(.ptr_cast, result_ptr_ty, uncasted_ptr);
25403 }
25404}
25405
25406fn zirWorkItem(
25407 sema: *Sema,
25408 block: *Block,
25409 extended: Zir.Inst.Extended.InstData,
25410 zir_tag: Zir.Inst.Extended,
25411) CompileError!Air.Inst.Ref {
25412 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
25413 const dimension_src = block.builtinCallArgSrc(extra.node, 0);
25414 const builtin_src = block.nodeOffset(extra.node);
25415 const target = sema.pt.zcu.getTarget();
25416
25417 switch (target.cpu.arch) {
25418 // TODO: Allow for other GPU targets.
25419 .amdgcn, .spirv64, .spirv32, .nvptx, .nvptx64 => {},
25420 else => {
25421 return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)});
25422 },
25423 }
25424
25425 const dimension: u32 = @intCast(try sema.resolveInt(block, dimension_src, extra.operand, .u32, .{ .simple = .work_group_dim_index }));
25426 try sema.requireRuntimeBlock(block, builtin_src, null);
25427
25428 return block.addInst(.{
25429 .tag = switch (zir_tag) {
25430 .work_item_id => .work_item_id,
25431 .work_group_size => .work_group_size,
25432 .work_group_id => .work_group_id,
25433 else => unreachable,
25434 },
25435 .data = .{ .pl_op = .{
25436 .operand = .none,
25437 .payload = dimension,
25438 } },
25439 });
25440}
25441
25442fn zirInComptime(
25443 sema: *Sema,
25444 block: *Block,
25445) CompileError!Air.Inst.Ref {
25446 _ = sema;
25447 return if (block.isComptime()) .bool_true else .bool_false;
25448}
25449
25450fn zirStdLangValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
25451 const pt = sema.pt;
25452 const zcu = pt.zcu;
25453 const comp = zcu.comp;
25454 const gpa = comp.gpa;
25455 const io = comp.io;
25456 const ip = &zcu.intern_pool;
25457
25458 const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand))));
25459 const src = block.nodeOffset(src_node);
25460 const value: Zir.Inst.StdLangValue = @fromBackingInt(@intCast(extended.small));
25461
25462 const std_lang_type: Zcu.StdLangDecl = switch (value) {
25463 // zig fmt: off
25464 .atomic_order => .AtomicOrder,
25465 .atomic_rmw_op => .AtomicRmwOp,
25466 .calling_convention => .CallingConvention,
25467 .address_space => .AddressSpace,
25468 .float_mode => .FloatMode,
25469 .signedness => .Signedness,
25470 .reduce_op => .ReduceOp,
25471 .call_modifier => .CallModifier,
25472 .prefetch_options => .PrefetchOptions,
25473 .export_options => .ExportOptions,
25474 .extern_options => .ExternOptions,
25475 .branch_hint => .BranchHint,
25476 .clobbers => .@"assembly.Clobbers",
25477 .pointer_size => .@"Type.Pointer.Size",
25478 .pointer_attributes => .@"Type.Pointer.Attributes",
25479 .fn_attributes, => .@"Type.Fn.Attributes",
25480 .container_layout => .@"Type.ContainerLayout",
25481 .enum_mode => .@"Type.Enum.Mode",
25482 .spirv_type_options => .@"Type.Spirv",
25483 // zig fmt: on
25484
25485 // Values are handled here.
25486 .calling_convention_c => {
25487 const callconv_ty = try sema.getStdLangType(src, .CallingConvention);
25488 // Cannot use `Value.uninterpret` because `c` is a *declaration* whose value depends on the target.
25489 return try sema.namespaceLookupVal(
25490 block,
25491 src,
25492 callconv_ty.getNamespaceIndex(zcu),
25493 try ip.getOrPutString(gpa, io, pt.tid, "c", .no_embedded_nulls),
25494 ) orelse @panic("std.lang is corrupt");
25495 },
25496 .calling_convention_inline => {
25497 const callconv_ty = try sema.getStdLangType(src, .CallingConvention);
25498 return .fromValue(Value.uninterpret(
25499 @as(std.lang.CallingConvention, .@"inline"),
25500 callconv_ty,
25501 pt,
25502 ) catch |err| switch (err) {
25503 error.TypeMismatch => @panic("std.lang is corrupt"),
25504 error.OutOfMemory => |e| return e,
25505 });
25506 },
25507 };
25508 return .fromType(try sema.getStdLangType(src, std_lang_type));
25509}
25510
25511fn zirInplaceArithResultTy(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
25512 const pt = sema.pt;
25513 const zcu = pt.zcu;
25514
25515 const lhs = sema.resolveInst(@fromBackingInt(@intCast(extended.operand)));
25516 const lhs_ty = sema.typeOf(lhs);
25517
25518 const op: Zir.Inst.InplaceOp = @fromBackingInt(@intCast(extended.small));
25519 const ty: Type = switch (op) {
25520 .add_eq => ty: {
25521 const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty;
25522 switch (ptr_size) {
25523 .one, .slice => break :ty lhs_ty, // invalid, let it error
25524 .many, .c => break :ty .usize, // `[*]T + usize`
25525 }
25526 },
25527 .sub_eq => ty: {
25528 const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty;
25529 switch (ptr_size) {
25530 .one, .slice => break :ty lhs_ty, // invalid, let it error
25531 .many, .c => break :ty .generic_poison, // could be `[*]T - [*]T` or `[*]T - usize`
25532 }
25533 },
25534 };
25535 return Air.internedToRef(ty.toIntern());
25536}
25537
25538fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
25539 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
25540 const uncoerced_hint = sema.resolveInst(extra.operand);
25541 const operand_src = block.builtinCallArgSrc(extra.node, 0);
25542
25543 const hint_ty = try sema.getStdLangType(operand_src, .BranchHint);
25544 const coerced_hint = try sema.coerce(block, hint_ty, uncoerced_hint, operand_src);
25545 const hint_val = try sema.resolveConstDefinedValue(block, operand_src, coerced_hint, .{ .simple = .operand_branchHint });
25546
25547 // We only apply the first hint in a branch.
25548 // This allows user-provided hints to override implicit cold hints.
25549 if (sema.branch_hint == null) {
25550 sema.branch_hint = try sema.interpretStdLangType(block, operand_src, hint_val, std.lang.BranchHint);
25551 }
25552}
25553
25554fn zirFloatOpResultType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
25555 const pt = sema.pt;
25556 const zcu = pt.zcu;
25557 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
25558 const operand_src = block.builtinCallArgSrc(extra.node, 0);
25559
25560 const raw_ty = try sema.resolveTypeOrPoison(block, operand_src, extra.operand) orelse return .generic_poison_type;
25561 const float_ty = raw_ty.optEuBaseType(zcu);
25562
25563 switch (float_ty.scalarType(zcu).zigTypeTag(zcu)) {
25564 .float, .comptime_float => {},
25565 else => return sema.fail(
25566 block,
25567 operand_src,
25568 "expected vector of floats or float type, found '{f}'",
25569 .{float_ty.fmt(sema.pt)},
25570 ),
25571 }
25572
25573 return .fromType(float_ty);
25574}
25575
25576fn zirRoundOpType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
25577 const pt = sema.pt;
25578 const zcu = pt.zcu;
25579 const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
25580 const operand_src = block.builtinCallArgSrc(extra.node, 0);
25581
25582 const dest_ty = try sema.resolveTypeOrPoison(block, operand_src, extra.operand) orelse {
25583 return .generic_poison_type;
25584 };
25585
25586 const dest_base_ty = dest_ty.optEuBaseType(zcu);
25587 switch (dest_base_ty.scalarType(zcu).zigTypeTag(zcu)) {
25588 .float, .comptime_float => return .fromType(dest_base_ty),
25589 else => return .generic_poison_type,
25590 }
25591}
25592
25593fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
25594 if (block.isComptime()) {
25595 const msg, const fail_block = msg: {
25596 const msg = try sema.errMsg(src, "unable to evaluate comptime expression", .{});
25597 errdefer msg.destroy(sema.gpa);
25598
25599 if (runtime_src) |some| {
25600 try sema.errNote(some, msg, "operation is runtime due to this operand", .{});
25601 }
25602
25603 const fail_block = try block.explainWhyBlockIsComptime(msg);
25604
25605 break :msg .{ msg, fail_block };
25606 };
25607 return sema.failWithOwnedErrorMsg(fail_block, msg);
25608 }
25609}
25610
25611/// Emit a compile error if `var_ty` cannot be used for a runtime variable.
25612/// Asserts that the layout of `var_ty` is already resolved.
25613pub fn validateVarType(
25614 sema: *Sema,
25615 block: *Block,
25616 src: LazySrcLoc,
25617 var_ty: Type,
25618 is_extern: bool,
25619) CompileError!void {
25620 const pt = sema.pt;
25621 const zcu = pt.zcu;
25622 var_ty.assertHasLayout(zcu);
25623 if (is_extern) {
25624 if (!var_ty.validateExtern(.other, zcu)) {
25625 const msg = msg: {
25626 const msg = try sema.errMsg(src, "extern variable cannot have type '{f}'", .{var_ty.fmt(pt)});
25627 errdefer msg.destroy(sema.gpa);
25628 try sema.explainWhyTypeIsNotExtern(msg, src, var_ty, .other);
25629 break :msg msg;
25630 };
25631 return sema.failWithOwnedErrorMsg(block, msg);
25632 }
25633 } else {
25634 if (var_ty.zigTypeTag(zcu) == .@"opaque") {
25635 return sema.fail(
25636 block,
25637 src,
25638 "non-extern variable with opaque type '{f}'",
25639 .{var_ty.fmt(pt)},
25640 );
25641 }
25642 }
25643
25644 if (!var_ty.comptimeOnly(zcu)) return;
25645
25646 const msg = msg: {
25647 const msg = try sema.errMsg(src, "variable of type '{f}' must be const or comptime", .{var_ty.fmt(pt)});
25648 errdefer msg.destroy(sema.gpa);
25649
25650 try sema.explainWhyTypeIsComptime(msg, src, var_ty);
25651 if (var_ty.zigTypeTag(zcu) == .comptime_int or var_ty.zigTypeTag(zcu) == .comptime_float) {
25652 try sema.errNote(src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{});
25653 }
25654
25655 break :msg msg;
25656 };
25657 return sema.failWithOwnedErrorMsg(block, msg);
25658}
25659
25660fn explainWhyTypeIsComptime(
25661 sema: *Sema,
25662 msg: *Zcu.ErrorMsg,
25663 src: LazySrcLoc,
25664 ty: Type,
25665) CompileError!void {
25666 const pt = sema.pt;
25667 const zcu = pt.zcu;
25668 const ip = &zcu.intern_pool;
25669 assert(ty.comptimeOnly(zcu));
25670 switch (ty.zigTypeTag(zcu)) {
25671 .bool,
25672 .int,
25673 .float,
25674 .error_set,
25675 .frame,
25676 .@"anyframe",
25677 .void,
25678 .@"enum",
25679 .@"opaque",
25680 .spirv,
25681 .pointer,
25682 => unreachable, // not comptime-only
25683
25684 .comptime_float,
25685 .comptime_int,
25686 .enum_literal,
25687 .noreturn,
25688 .undefined,
25689 .null,
25690 => return, // no explanation needed
25691
25692 .array, .vector => try sema.explainWhyTypeIsComptime(msg, src, ty.childType(zcu)),
25693 .optional => try sema.explainWhyTypeIsComptime(msg, src, ty.optionalChild(zcu)),
25694 .error_union => try sema.explainWhyTypeIsComptime(msg, src, ty.errorUnionPayload(zcu)),
25695
25696 .@"fn" => try sema.errNote(src, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)}),
25697 .type => try sema.errNote(src, msg, "types are not available at runtime", .{}),
25698
25699 .@"struct" => if (zcu.typeToStruct(ty)) |struct_type| {
25700 ty.assertHasLayout(zcu);
25701 for (0..struct_type.field_types.len) |i| {
25702 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]);
25703 if (!field_ty.comptimeOnly(zcu)) continue;
25704 const field_src: LazySrcLoc = .{
25705 .base_node_inst = struct_type.zir_index,
25706 .offset = .{ .container_field_type = @intCast(i) },
25707 };
25708 try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{});
25709 return sema.explainWhyTypeIsComptime(msg, field_src, field_ty);
25710 }
25711 unreachable;
25712 } else {
25713 const tuple = ip.indexToKey(ty.toIntern()).tuple_type;
25714 for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty_ip, field_val_ip| {
25715 if (field_val_ip != .none) continue;
25716 const field_ty: Type = .fromInterned(field_ty_ip);
25717 if (!field_ty.comptimeOnly(zcu)) continue;
25718 try sema.errNote(src, msg, "tuple requires comptime because of field of type '{f}'", .{field_ty.fmt(pt)});
25719 return sema.explainWhyTypeIsComptime(msg, src, field_ty);
25720 }
25721 unreachable;
25722 },
25723
25724 .@"union" => {
25725 const union_obj = zcu.typeToUnion(ty).?;
25726 for (0..union_obj.field_types.len) |i| {
25727 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]);
25728 if (!field_ty.comptimeOnly(zcu)) continue;
25729 const field_src: LazySrcLoc = .{
25730 .base_node_inst = union_obj.zir_index,
25731 .offset = .{ .container_field_type = @intCast(i) },
25732 };
25733 try sema.errNote(field_src, msg, "union requires comptime because of this field", .{});
25734 return sema.explainWhyTypeIsComptime(msg, field_src, field_ty);
25735 }
25736 unreachable;
25737 },
25738 }
25739}
25740
25741/// Keep in sync with `Type.validateExtern`.
25742pub fn explainWhyTypeIsNotExtern(
25743 sema: *Sema,
25744 msg: *Zcu.ErrorMsg,
25745 src_loc: LazySrcLoc,
25746 ty: Type,
25747 position: Type.ExternPosition,
25748) SemaError!void {
25749 const pt = sema.pt;
25750 const zcu = pt.zcu;
25751 switch (ty.zigTypeTag(zcu)) {
25752 .type,
25753 .comptime_float,
25754 .comptime_int,
25755 .enum_literal,
25756 .undefined,
25757 .null,
25758 .error_union,
25759 .error_set,
25760 .frame,
25761 => return,
25762
25763 .void => try sema.errNote(src_loc, msg, "'void' is a zero bit type", .{}),
25764 .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}),
25765
25766 .@"opaque",
25767 .bool,
25768 .@"anyframe",
25769 => unreachable, // these *are* allowed
25770
25771 .spirv => {
25772 assert(ty.isSpirvRuntimeArray(zcu));
25773 try sema.errNote(src_loc, msg, "SPIR-V runtime arrays must be the last field of an extern struct", .{});
25774 if (position == .other) {
25775 try sema.errNote(src_loc, msg, "consider enabling the 'runtime_descriptor_array' feature to use the runtime array as the extern pointee", .{});
25776 }
25777 },
25778
25779 .float => try sema.errNote(src_loc, msg, "'{f}' is not extern compatible on this target", .{ty.fmt(pt)}),
25780 .pointer => if (ty.isSlice(zcu)) {
25781 try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{});
25782 } else {
25783 assert(ty.childType(zcu).zigTypeTag(zcu) == .@"fn");
25784 if (!ty.isConstPtr(zcu)) {
25785 try sema.errNote(src_loc, msg, "pointer to extern function must be 'const'", .{});
25786 } else {
25787 try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.childType(zcu), .other);
25788 }
25789 },
25790 .int => if (!std.math.isPowerOfTwo(ty.intInfo(zcu).bits)) {
25791 try sema.errNote(src_loc, msg, "only integers with 0 or power of two bits are extern compatible", .{});
25792 } else {
25793 try sema.errNote(src_loc, msg, "only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible", .{});
25794 },
25795 .@"fn" => if (position != .other) {
25796 try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{});
25797 try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{});
25798 } else switch (ty.fnCallingConvention(zcu)) {
25799 .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}),
25800 else => |cc| try sema.errNote(src_loc, msg, "{t} function cannot be extern", .{cc}),
25801 },
25802 .@"enum" => {
25803 const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern());
25804 switch (enum_obj.int_tag_mode) {
25805 .auto => {
25806 try sema.errNote(ty.srcLoc(zcu), msg, "integer tag type of enum is inferred", .{});
25807 try sema.errNote(ty.srcLoc(zcu), msg, "consider explicitly specifying the integer tag type", .{});
25808 },
25809 .explicit => {
25810 const tag_ty: Type = .fromInterned(enum_obj.int_tag_type);
25811 try sema.errNote(ty.srcLoc(zcu), msg, "enum tag type '{f}' is not extern compatible", .{tag_ty.fmt(pt)});
25812 try sema.explainWhyTypeIsNotExtern(msg, ty.srcLoc(zcu), tag_ty, position);
25813 },
25814 }
25815 },
25816 .@"struct" => {
25817 if (ty.isTuple(zcu)) {
25818 return sema.errNote(src_loc, msg, "tuples have no guaranteed in-memory representation", .{});
25819 }
25820
25821 const struct_obj = zcu.intern_pool.loadStructType(ty.toIntern());
25822 switch (struct_obj.layout) {
25823 .auto => try sema.errNote(src_loc, msg, "struct with automatic layout has no guaranteed in-memory representation", .{}),
25824 .@"extern" => unreachable,
25825 .@"packed" => switch (struct_obj.packed_backing_mode) {
25826 .auto => try sema.errNote(src_loc, msg, "inferred backing integer of packed struct has unspecified signedness", .{}),
25827 .explicit => {
25828 const backing_int_ty: Type = .fromInterned(struct_obj.packed_backing_int_type);
25829 try sema.errNote(src_loc, msg, "packed struct backing integer type '{f}' is not extern compatible", .{backing_int_ty.fmt(pt)});
25830 try sema.explainWhyTypeIsNotExtern(msg, src_loc, backing_int_ty, position);
25831 },
25832 },
25833 }
25834 },
25835 .@"union" => {
25836 const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern());
25837 switch (union_obj.layout) {
25838 .auto => try sema.errNote(src_loc, msg, "union with automatic layout has no guaranteed in-memory representation", .{}),
25839 .@"extern" => unreachable,
25840 .@"packed" => switch (union_obj.packed_backing_mode) {
25841 .auto => try sema.errNote(src_loc, msg, "inferred backing integer of packed union has unspecified signedness", .{}),
25842 .explicit => {
25843 const backing_int_ty: Type = .fromInterned(union_obj.packed_backing_int_type);
25844 try sema.errNote(src_loc, msg, "packed union backing integer type '{f}' is not extern compatible", .{backing_int_ty.fmt(pt)});
25845 try sema.explainWhyTypeIsNotExtern(msg, src_loc, backing_int_ty, position);
25846 },
25847 },
25848 }
25849 },
25850 .array => switch (position) {
25851 .ret_ty => try sema.errNote(src_loc, msg, "arrays are not allowed as a return type", .{}),
25852 .param_ty => try sema.errNote(src_loc, msg, "arrays are not allowed as a parameter type", .{}),
25853 else => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.childType(zcu), .element),
25854 },
25855 .vector => try sema.errNote(src_loc, msg, "vectors have no guaranteed in-memory representation", .{}),
25856 .optional => try sema.errNote(src_loc, msg, "non-pointer optionals have no guaranteed in-memory representation", .{}),
25857 }
25858}
25859
25860pub fn explainWhyTypeIsUnpackable(
25861 sema: *Sema,
25862 msg: *Zcu.ErrorMsg,
25863 src: LazySrcLoc,
25864 reason: Type.UnpackableReason,
25865) CompileError!void {
25866 const pt = sema.pt;
25867 const zcu = pt.zcu;
25868 switch (reason) {
25869 .comptime_only => try sema.errNote(src, msg, "comptime-only types have no bit-packed representation", .{}),
25870 .pointer => {
25871 try sema.errNote(src, msg, "pointers cannot be directly bitpacked", .{});
25872 try sema.errNote(src, msg, "consider using 'usize' and '@intFromPtr'", .{});
25873 },
25874 .enum_inferred_int_tag => |enum_ty| {
25875 const enum_src = enum_ty.srcLoc(zcu);
25876 try sema.errNote(enum_src, msg, "integer tag type of enum is inferred", .{});
25877 try sema.errNote(enum_src, msg, "consider explicitly specifying the integer tag type", .{});
25878 },
25879 .non_packed_struct => |struct_ty| {
25880 try sema.errNote(src, msg, "non-packed structs do not have a bit-packed representation", .{});
25881 try sema.addDeclaredHereNote(msg, struct_ty);
25882 },
25883 .non_packed_union => |union_ty| {
25884 try sema.errNote(src, msg, "non-packed unions do not have a bit-packed representation", .{});
25885 try sema.addDeclaredHereNote(msg, union_ty);
25886 },
25887 .slice => try sema.errNote(src, msg, "slices do not have a bit-packed representation", .{}),
25888 .other => try sema.errNote(src, msg, "type does not have a bit-packed representation", .{}),
25889 }
25890}
25891
25892/// Backends depend on panic decls being available when lowering safety-checked
25893/// instructions. This function ensures the panic function will be available to
25894/// be called during that time.
25895fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !void {
25896 const zcu = sema.pt.zcu;
25897
25898 // If the backend doesn't support `.panic_fn`, it doesn't want us to lower the panic handlers.
25899 // The backend will transform panics into traps instead.
25900 if (!zcu.backendSupportsFeature(.panic_fn)) return;
25901
25902 const fn_index = try sema.getPanicIdFunc(src, panic_id);
25903 const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(fn_index);
25904 try sema.addReferenceEntry(null, src, .wrap(.{ .func = orig_fn_index }));
25905 try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
25906}
25907
25908fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index {
25909 const zcu = sema.pt.zcu;
25910 const io = zcu.comp.io;
25911 try sema.ensureMemoizedStateResolved(src, .panic);
25912 const panic_fn_index = zcu.std_lang_decl_values.get(panic_id.toStdLangDecl());
25913 switch (sema.owner.unwrap()) {
25914 .@"comptime",
25915 .nav_ty,
25916 .nav_val,
25917 .type_layout,
25918 .struct_defaults,
25919 .memoized_state,
25920 => {},
25921
25922 .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(io, owner_func, true),
25923 }
25924 return panic_fn_index;
25925}
25926
25927fn addSafetyCheck(
25928 sema: *Sema,
25929 parent_block: *Block,
25930 src: LazySrcLoc,
25931 ok: Air.Inst.Ref,
25932 panic_id: Zcu.SimplePanicId,
25933) !void {
25934 const gpa = sema.gpa;
25935 assert(!parent_block.isComptime());
25936
25937 var fail_block: Block = .{
25938 .parent = parent_block,
25939 .sema = sema,
25940 .namespace = parent_block.namespace,
25941 .instructions = .empty,
25942 .inlining = parent_block.inlining,
25943 .comptime_reason = null,
25944 .src_base_inst = parent_block.src_base_inst,
25945 .type_name_ctx = parent_block.type_name_ctx,
25946 .type_fqn_ctx = parent_block.type_fqn_ctx,
25947 };
25948
25949 defer fail_block.instructions.deinit(gpa);
25950
25951 try sema.safetyPanic(&fail_block, src, panic_id);
25952 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
25953}
25954
25955fn addSafetyCheckExtra(
25956 sema: *Sema,
25957 parent_block: *Block,
25958 ok: Air.Inst.Ref,
25959 fail_block: *Block,
25960) !void {
25961 const gpa = sema.gpa;
25962
25963 try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
25964
25965 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len +
25966 1 + // The main block only needs space for the cond_br.
25967 @typeInfo(Air.CondBr).@"struct".field_names.len +
25968 1 + // The ok branch of the cond_br only needs space for the br.
25969 fail_block.instructions.items.len);
25970
25971 try sema.air_instructions.ensureUnusedCapacity(gpa, 3);
25972 const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len));
25973 const cond_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(@backingInt(block_inst) + 1));
25974 const br_inst: Air.Inst.Index = @fromBackingInt(@intCast(@backingInt(cond_br_inst) + 1));
25975 sema.air_instructions.appendAssumeCapacity(.{
25976 .tag = .block,
25977 .data = .{ .ty_pl = .{
25978 .ty = .void,
25979 .payload = sema.addExtraAssumeCapacity(Air.Block{
25980 .body_len = 1,
25981 }),
25982 } },
25983 });
25984 sema.air_extra.appendAssumeCapacity(@backingInt(cond_br_inst));
25985
25986 sema.air_instructions.appendAssumeCapacity(.{
25987 .tag = .cond_br,
25988 .data = .{
25989 .pl_op = .{
25990 .operand = ok,
25991 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
25992 .then_body_len = 1,
25993 .else_body_len = @intCast(fail_block.instructions.items.len),
25994 .branch_hints = .{
25995 // Safety check failure branch is cold.
25996 .true = .likely,
25997 .false = .cold,
25998 // Code coverage not wanted for panic branches.
25999 .then_cov = .none,
26000 .else_cov = .none,
26001 },
26002 }),
26003 },
26004 },
26005 });
26006 sema.air_extra.appendAssumeCapacity(@backingInt(br_inst));
26007 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(fail_block.instructions.items));
26008
26009 sema.air_instructions.appendAssumeCapacity(.{
26010 .tag = .br,
26011 .data = .{ .br = .{
26012 .block_inst = block_inst,
26013 .operand = .void_value,
26014 } },
26015 });
26016
26017 parent_block.instructions.appendAssumeCapacity(block_inst);
26018}
26019
26020fn addSafetyCheckUnwrapError(
26021 sema: *Sema,
26022 parent_block: *Block,
26023 src: LazySrcLoc,
26024 operand: Air.Inst.Ref,
26025 unwrap_err_tag: Air.Inst.Tag,
26026 is_non_err_tag: Air.Inst.Tag,
26027) !void {
26028 assert(!parent_block.isComptime());
26029 const ok = try parent_block.addUnOp(is_non_err_tag, operand);
26030 const gpa = sema.gpa;
26031
26032 var fail_block: Block = .{
26033 .parent = parent_block,
26034 .sema = sema,
26035 .namespace = parent_block.namespace,
26036 .instructions = .empty,
26037 .inlining = parent_block.inlining,
26038 .comptime_reason = null,
26039 .src_base_inst = parent_block.src_base_inst,
26040 .type_name_ctx = parent_block.type_name_ctx,
26041 .type_fqn_ctx = parent_block.type_fqn_ctx,
26042 };
26043
26044 defer fail_block.instructions.deinit(gpa);
26045
26046 const err = try fail_block.addTyOp(unwrap_err_tag, .anyerror, operand);
26047 try safetyPanicUnwrapError(sema, &fail_block, src, err);
26048
26049 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
26050}
26051
26052fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air.Inst.Ref) !void {
26053 const pt = sema.pt;
26054 const zcu = pt.zcu;
26055 if (!zcu.backendSupportsFeature(.panic_fn)) {
26056 _ = try block.addNoOp(.trap);
26057 } else {
26058 const panic_fn = try getStdLangValue(sema, src, .@"panic.unwrapError");
26059 try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{err}, .@"safety check");
26060 }
26061}
26062
26063fn addSafetyCheckIndexOob(
26064 sema: *Sema,
26065 parent_block: *Block,
26066 src: LazySrcLoc,
26067 index: Air.Inst.Ref,
26068 len: Air.Inst.Ref,
26069 cmp_op: Air.Inst.Tag,
26070) !void {
26071 assert(!parent_block.isComptime());
26072 const ok = try parent_block.addBinOp(cmp_op, index, len);
26073 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.outOfBounds", &.{ index, len });
26074}
26075
26076fn addSafetyCheckInactiveUnionField(
26077 sema: *Sema,
26078 parent_block: *Block,
26079 src: LazySrcLoc,
26080 active_tag: Air.Inst.Ref,
26081 wanted_tag: Air.Inst.Ref,
26082) !void {
26083 assert(!parent_block.isComptime());
26084 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
26085 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.inactiveUnionField", &.{ active_tag, wanted_tag });
26086}
26087
26088fn addSafetyCheckSentinelMismatch(
26089 sema: *Sema,
26090 parent_block: *Block,
26091 src: LazySrcLoc,
26092 maybe_sentinel: ?Value,
26093 sentinel_ty: Type,
26094 ptr: Air.Inst.Ref,
26095 sentinel_index: Air.Inst.Ref,
26096) !void {
26097 assert(!parent_block.isComptime());
26098 const pt = sema.pt;
26099 const zcu = pt.zcu;
26100 const expected_sentinel_val = maybe_sentinel orelse return;
26101 const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern());
26102
26103 const ptr_ty = sema.typeOf(ptr);
26104 const ptr_info = ptr_ty.ptrInfo(zcu);
26105 const actual_sentinel: Air.Inst.Ref = switch (ptr_ty.ptrSize(zcu)) {
26106 .slice => try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index),
26107 .one => s: {
26108 const array_ty: Type = .fromInterned(ptr_info.child);
26109 assert(array_ty.zigTypeTag(zcu) == .array);
26110 assert(array_ty.childType(zcu).toIntern() == sentinel_ty.toIntern());
26111 const many_ptr_ty = try pt.ptrType(.{
26112 .child = sentinel_ty.toIntern(),
26113 .flags = .{
26114 .size = .many,
26115 .is_const = ptr_info.flags.is_const,
26116 .is_volatile = ptr_info.flags.is_volatile,
26117 .is_allowzero = ptr_info.flags.is_allowzero,
26118 .alignment = switch (ptr_info.flags.alignment) {
26119 .none => .none,
26120 else => |ptr_align| .minStrict(ptr_align, sentinel_ty.abiAlignment(zcu)),
26121 },
26122 .address_space = ptr_info.flags.address_space,
26123 },
26124 });
26125 const many_ptr = try parent_block.addTyOp(.ptr_cast, many_ptr_ty, ptr);
26126 break :s try parent_block.addBinOp(.ptr_elem_val, many_ptr, sentinel_index);
26127 },
26128 .many => unreachable,
26129 .c => unreachable,
26130 };
26131 assert(sema.typeOf(actual_sentinel).toIntern() == sentinel_ty.toIntern());
26132 assert(sentinel_ty.isSelfComparable(zcu, true));
26133 const ok: Air.Inst.Ref = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: {
26134 const elementwise = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq);
26135 break :ok try parent_block.addReduce(elementwise, .And);
26136 } else try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel);
26137
26138 return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{
26139 expected_sentinel, actual_sentinel,
26140 });
26141}
26142
26143fn addSafetyCheckCall(
26144 sema: *Sema,
26145 parent_block: *Block,
26146 src: LazySrcLoc,
26147 ok: Air.Inst.Ref,
26148 comptime func_decl: Zcu.StdLangDecl,
26149 args: []const Air.Inst.Ref,
26150) !void {
26151 assert(!parent_block.isComptime());
26152 const gpa = sema.gpa;
26153 const pt = sema.pt;
26154 const zcu = pt.zcu;
26155
26156 var fail_block: Block = .{
26157 .parent = parent_block,
26158 .sema = sema,
26159 .namespace = parent_block.namespace,
26160 .instructions = .empty,
26161 .inlining = parent_block.inlining,
26162 .comptime_reason = null,
26163 .src_base_inst = parent_block.src_base_inst,
26164 .type_name_ctx = parent_block.type_name_ctx,
26165 .type_fqn_ctx = parent_block.type_fqn_ctx,
26166 };
26167
26168 defer fail_block.instructions.deinit(gpa);
26169
26170 if (!zcu.backendSupportsFeature(.panic_fn)) {
26171 _ = try fail_block.addNoOp(.trap);
26172 } else {
26173 const panic_fn = try getStdLangValue(sema, src, func_decl);
26174 try sema.callBuiltin(&fail_block, src, Air.internedToRef(panic_fn), .auto, args, .@"safety check");
26175 }
26176
26177 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
26178}
26179
26180/// This does not set `sema.branch_hint`.
26181fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) CompileError!void {
26182 if (!sema.pt.zcu.backendSupportsFeature(.panic_fn)) {
26183 _ = try block.addNoOp(.trap);
26184 } else {
26185 const panic_fn = try sema.getPanicIdFunc(src, panic_id);
26186 try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{}, .@"safety check");
26187 }
26188}
26189
26190fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void {
26191 sema.branch_count += 1;
26192 if (sema.branch_count > sema.branch_quota) {
26193 const msg = msg: {
26194 const msg = try sema.errMsg(
26195 src,
26196 "evaluation exceeded {d} backwards branches",
26197 .{sema.branch_quota},
26198 );
26199 errdefer msg.destroy(sema.gpa);
26200 try sema.errNote(
26201 src,
26202 msg,
26203 "use @setEvalBranchQuota() to raise the branch limit from {d}",
26204 .{sema.branch_quota},
26205 );
26206 break :msg msg;
26207 };
26208 return sema.failWithOwnedErrorMsg(block, msg);
26209 }
26210}
26211
26212fn fieldPtrLoad(
26213 sema: *Sema,
26214 block: *Block,
26215 src: LazySrcLoc,
26216 object_ptr: Air.Inst.Ref,
26217 field_name: InternPool.NullTerminatedString,
26218 field_name_src: LazySrcLoc,
26219) CompileError!Air.Inst.Ref {
26220 const pt = sema.pt;
26221 const zcu = pt.zcu;
26222 const ip = &zcu.intern_pool;
26223 const object_ptr_ty = sema.typeOf(object_ptr);
26224 assert(object_ptr_ty.zigTypeTag(zcu) == .pointer);
26225 const pointee_ty = object_ptr_ty.childType(zcu);
26226 if (pointee_ty.isSpirvRuntimeArray(zcu) and field_name.eqlSlice("len", ip)) {
26227 return sema.analyzeSpirvRuntimeArrayLen(block, src, object_ptr, field_name_src);
26228 }
26229 try sema.ensureLayoutResolved(pointee_ty, src, .ptr_access);
26230 if (try pointee_ty.onePossibleValue(pt)) |opv| {
26231 const object: Air.Inst.Ref = .fromValue(opv);
26232 return fieldVal(sema, block, src, object, field_name, field_name_src);
26233 }
26234
26235 if (try sema.resolveDefinedValue(block, src, object_ptr)) |object_ptr_val| {
26236 if (try sema.pointerDeref(block, src, object_ptr_val, object_ptr_ty)) |object_val| {
26237 const object: Air.Inst.Ref = .fromValue(object_val);
26238 return fieldVal(sema, block, src, object, field_name, field_name_src);
26239 }
26240 }
26241 const field_ptr = try sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false);
26242 return analyzeLoad(sema, block, src, field_ptr, field_name_src);
26243}
26244
26245fn fieldVal(
26246 sema: *Sema,
26247 block: *Block,
26248 src: LazySrcLoc,
26249 object: Air.Inst.Ref,
26250 field_name: InternPool.NullTerminatedString,
26251 field_name_src: LazySrcLoc,
26252) CompileError!Air.Inst.Ref {
26253 // When editing this function, note that there is corresponding logic to be edited
26254 // in `fieldPtr`. This function takes a value and returns a value.
26255
26256 const pt = sema.pt;
26257 const zcu = pt.zcu;
26258 const ip = &zcu.intern_pool;
26259 const object_src = src; // TODO better source location
26260 const object_ty = sema.typeOf(object);
26261
26262 // Zig allows dereferencing a single pointer during field lookup. Note that
26263 // we don't actually need to generate the dereference some field lookups, like the
26264 // length of arrays and other comptime operations.
26265 const is_pointer_to = object_ty.isSinglePointer(zcu);
26266
26267 const inner_ty = if (is_pointer_to)
26268 object_ty.childType(zcu)
26269 else
26270 object_ty;
26271
26272 switch (inner_ty.zigTypeTag(zcu)) {
26273 .array => {
26274 if (field_name.eqlSlice("len", ip)) {
26275 return Air.internedToRef((try pt.intValue(.usize, inner_ty.arrayLen(zcu))).toIntern());
26276 } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
26277 const ptr_info = object_ty.ptrInfo(zcu);
26278 const result_ty = try pt.ptrType(.{
26279 .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(),
26280 .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none,
26281 .flags = .{
26282 .size = .many,
26283 .alignment = ptr_info.flags.alignment,
26284 .is_const = ptr_info.flags.is_const,
26285 .is_volatile = ptr_info.flags.is_volatile,
26286 .is_allowzero = ptr_info.flags.is_allowzero,
26287 .address_space = ptr_info.flags.address_space,
26288 .vector_index = ptr_info.flags.vector_index,
26289 },
26290 .packed_offset = ptr_info.packed_offset,
26291 });
26292 return sema.coerce(block, result_ty, object, src);
26293 } else {
26294 return sema.fail(
26295 block,
26296 field_name_src,
26297 "no member named '{f}' in '{f}'",
26298 .{ field_name.fmt(ip), object_ty.fmt(pt) },
26299 );
26300 }
26301 },
26302 .pointer => {
26303 const ptr_info = inner_ty.ptrInfo(zcu);
26304 if (ptr_info.flags.size == .slice) {
26305 if (field_name.eqlSlice("ptr", ip)) {
26306 const slice = if (is_pointer_to)
26307 try sema.analyzeLoad(block, src, object, object_src)
26308 else
26309 object;
26310 return sema.analyzeSlicePtr(block, object_src, slice, inner_ty);
26311 } else if (field_name.eqlSlice("len", ip)) {
26312 const slice = if (is_pointer_to)
26313 try sema.analyzeLoad(block, src, object, object_src)
26314 else
26315 object;
26316 return sema.analyzeSliceLen(block, src, slice);
26317 } else {
26318 return sema.fail(
26319 block,
26320 field_name_src,
26321 "no member named '{f}' in '{f}'",
26322 .{ field_name.fmt(ip), object_ty.fmt(pt) },
26323 );
26324 }
26325 }
26326 },
26327 .type => {
26328 const dereffed_type = if (is_pointer_to)
26329 try sema.analyzeLoad(block, src, object, object_src)
26330 else
26331 object;
26332
26333 const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?;
26334 const child_type = val.toType();
26335
26336 switch (child_type.zigTypeTag(zcu)) {
26337 .error_set => {
26338 const err_set_ty: Type = err_set: switch (ip.indexToKey(child_type.toIntern())) {
26339 .inferred_error_set_type => |func_index| {
26340 try sema.ensureFuncIesResolved(block, src, func_index);
26341 const resolved_ies = ip.funcIesResolvedUnordered(func_index);
26342 continue :err_set ip.indexToKey(resolved_ies);
26343 },
26344 .error_set_type => |err_set| if (err_set.nameIndex(ip, field_name) == null) {
26345 return sema.fail(block, src, "no error named '{f}' in '{f}'", .{
26346 field_name.fmt(ip), child_type.fmt(pt),
26347 });
26348 } else child_type,
26349 .simple_type => |t| {
26350 assert(t == .anyerror);
26351 _ = try pt.getErrorValue(field_name);
26352 break :err_set try pt.singleErrorSetType(field_name);
26353 },
26354 else => unreachable,
26355 };
26356 return .fromIntern(try pt.intern(.{ .err = .{
26357 .ty = err_set_ty.toIntern(),
26358 .name = field_name,
26359 } }));
26360 },
26361 .@"union" => {
26362 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
26363 return inst;
26364 }
26365 try sema.ensureLayoutResolved(child_type, src, .field_used);
26366 if (child_type.unionTagType(zcu)) |enum_ty| {
26367 if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| {
26368 const field_index: u32 = @intCast(field_index_usize);
26369 return Air.internedToRef((try pt.enumValueFieldIndex(enum_ty, field_index)).toIntern());
26370 }
26371 }
26372 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
26373 },
26374 .@"enum" => {
26375 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
26376 return inst;
26377 }
26378 try sema.ensureLayoutResolved(child_type, src, .field_used);
26379 const field_index_usize = child_type.enumFieldIndex(field_name, zcu) orelse
26380 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
26381 const field_index: u32 = @intCast(field_index_usize);
26382 const enum_val = try pt.enumValueFieldIndex(child_type, field_index);
26383 return Air.internedToRef(enum_val.toIntern());
26384 },
26385 .@"struct", .@"opaque" => {
26386 if (!child_type.isTuple(zcu) and child_type.toIntern() != .anyopaque_type) {
26387 if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
26388 return inst;
26389 }
26390 }
26391 return sema.failWithBadMemberAccess(block, child_type, src, field_name);
26392 },
26393 else => return sema.failWithOwnedErrorMsg(block, msg: {
26394 const msg = try sema.errMsg(src, "type '{f}' has no members", .{child_type.fmt(pt)});
26395 errdefer msg.destroy(sema.gpa);
26396 if (child_type.isSlice(zcu)) try sema.errNote(src, msg, "slice values have 'len' and 'ptr' members", .{});
26397 if (child_type.zigTypeTag(zcu) == .array) try sema.errNote(src, msg, "array values have 'len' member", .{});
26398 break :msg msg;
26399 }),
26400 }
26401 },
26402 .@"struct" => if (is_pointer_to) {
26403 // Avoid loading the entire struct by fetching a pointer and loading that
26404 try sema.ensureLayoutResolved(inner_ty, src, .ptr_access);
26405 const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty);
26406 return sema.analyzeLoad(block, src, field_ptr, object_src);
26407 } else {
26408 return sema.structFieldVal(block, object, field_name, field_name_src, inner_ty);
26409 },
26410 .@"union" => if (is_pointer_to) {
26411 // Avoid loading the entire union by fetching a pointer and loading that
26412 try sema.ensureLayoutResolved(inner_ty, src, .ptr_access);
26413 const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false);
26414 return sema.analyzeLoad(block, src, field_ptr, object_src);
26415 } else {
26416 return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty);
26417 },
26418 .spirv => if (inner_ty.isSpirvRuntimeArray(zcu) and field_name.eqlSlice("len", ip)) {
26419 if (!is_pointer_to) {
26420 return sema.fail(
26421 block,
26422 src,
26423 "accessing 'len' field on a SPIR-V runtime_array requires a pointer to the array field",
26424 .{},
26425 );
26426 }
26427 return sema.analyzeSpirvRuntimeArrayLen(block, src, object, field_name_src);
26428 },
26429 else => {},
26430 }
26431 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
26432}
26433
26434fn analyzeSpirvRuntimeArrayLen(
26435 sema: *Sema,
26436 block: *Block,
26437 src: LazySrcLoc,
26438 runtime_array_ptr: Air.Inst.Ref,
26439 src_for_err: LazySrcLoc,
26440) CompileError!Air.Inst.Ref {
26441 const pt = sema.pt;
26442 const zcu = pt.zcu;
26443 const ip = &zcu.intern_pool;
26444
26445 const struct_operand: Air.Inst.Ref, const field_index: u32 = sf: {
26446 if (runtime_array_ptr.toIndex()) |inst| {
26447 const tag = sema.air_instructions.items(.tag)[@backingInt(inst)];
26448 const data = sema.air_instructions.items(.data)[@backingInt(inst)];
26449 switch (tag) {
26450 .struct_field_ptr => {
26451 const extra = sema.getTmpAir().extraData(Air.StructField, data.ty_pl.payload).data;
26452 break :sf .{ extra.struct_operand, extra.field_index };
26453 },
26454 .struct_field_ptr_index_0 => break :sf .{ data.ty_op.operand, 0 },
26455 .struct_field_ptr_index_1 => break :sf .{ data.ty_op.operand, 1 },
26456 .struct_field_ptr_index_2 => break :sf .{ data.ty_op.operand, 2 },
26457 .struct_field_ptr_index_3 => break :sf .{ data.ty_op.operand, 3 },
26458 else => {},
26459 }
26460 }
26461
26462 const ptr_val = sema.resolveValue(runtime_array_ptr) orelse return sema.fail(
26463 block,
26464 src_for_err,
26465 "'len' field on a SPIR-V runtime_array requires direct struct field access",
26466 .{},
26467 );
26468 const ptr_key = ip.indexToKey(ptr_val.toIntern()).ptr;
26469 if (ptr_key.base_addr == .field and ptr_key.byte_offset == 0) {
26470 const field = ptr_key.base_addr.field;
26471 break :sf .{ .fromIntern(field.base), @intCast(field.index) };
26472 }
26473
26474 const parent_ty: Type = switch (ptr_key.base_addr) {
26475 .nav => |nav| .fromInterned(ip.getNav(nav).resolved.?.type),
26476 .uav => |uav| .fromInterned(ip.typeOf(uav.val)),
26477 .comptime_alloc,
26478 .comptime_field,
26479 .eu_payload,
26480 .opt_payload,
26481 .arr_elem,
26482 .field,
26483 .int,
26484 => return sema.fail(
26485 block,
26486 src_for_err,
26487 "'len' field on a SPIR-V runtime_array requires direct struct field access",
26488 .{},
26489 ),
26490 };
26491 if (parent_ty.zigTypeTag(zcu) != .@"struct") return sema.fail(
26492 block,
26493 src_for_err,
26494 "'len' field on a SPIR-V runtime_array requires the array to be a struct field",
26495 .{},
26496 );
26497
26498 const field_ptr_info = ip.indexToKey(ptr_key.ty).ptr_type;
26499 const rtarr_ty_ip = field_ptr_info.child;
26500 const struct_obj = ip.loadStructType(parent_ty.toIntern());
26501 const field_idx: u32 = for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, i| {
26502 if (field_ty_ip == rtarr_ty_ip and
26503 struct_obj.field_offsets.get(ip)[i] == ptr_key.byte_offset)
26504 {
26505 break @intCast(i);
26506 }
26507 } else unreachable;
26508 const struct_ptr_ty = try pt.ptrType(.{
26509 .child = parent_ty.toIntern(),
26510 .flags = field_ptr_info.flags,
26511 });
26512 const struct_ptr_val = try sema.ptrSubtract(
26513 block,
26514 src_for_err,
26515 ptr_val,
26516 ptr_key.byte_offset,
26517 struct_ptr_ty,
26518 );
26519 break :sf .{ .fromIntern(struct_ptr_val.toIntern()), field_idx };
26520 };
26521
26522 try sema.requireRuntimeBlock(block, src, null);
26523 return block.addInst(.{
26524 .tag = .spirv_runtime_array_len,
26525 .data = .{ .ty_pl = .{
26526 .ty = .u32,
26527 .payload = try sema.addExtra(Air.StructField{
26528 .struct_operand = struct_operand,
26529 .field_index = field_index,
26530 }),
26531 } },
26532 });
26533}
26534
26535fn fieldPtr(
26536 sema: *Sema,
26537 block: *Block,
26538 src: LazySrcLoc,
26539 object_ptr: Air.Inst.Ref,
26540 field_name: InternPool.NullTerminatedString,
26541 field_name_src: LazySrcLoc,
26542 initializing: bool,
26543) CompileError!Air.Inst.Ref {
26544 // When editing this function, note that there is corresponding logic to be edited
26545 // in `fieldVal`. This function takes a pointer and returns a pointer.
26546
26547 const pt = sema.pt;
26548 const zcu = pt.zcu;
26549 const ip = &zcu.intern_pool;
26550 const object_ptr_src = src; // TODO better source location
26551 const object_ptr_ty = sema.typeOf(object_ptr);
26552 const object_ty = switch (object_ptr_ty.zigTypeTag(zcu)) {
26553 .pointer => object_ptr_ty.childType(zcu),
26554 else => return sema.fail(block, object_ptr_src, "expected pointer, found '{f}'", .{object_ptr_ty.fmt(pt)}),
26555 };
26556
26557 // Zig allows dereferencing a single pointer during field lookup. Note that
26558 // we don't actually need to generate the dereference some field lookups, like the
26559 // length of arrays and other comptime operations.
26560 const is_pointer_to = object_ty.isSinglePointer(zcu);
26561
26562 const inner_ty = if (is_pointer_to)
26563 object_ty.childType(zcu)
26564 else
26565 object_ty;
26566
26567 switch (inner_ty.zigTypeTag(zcu)) {
26568 .array => {
26569 if (field_name.eqlSlice("len", ip)) {
26570 const int_val = try pt.intValue(.usize, inner_ty.arrayLen(zcu));
26571 return uavRef(sema, int_val);
26572 } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) {
26573 const ptr_info = object_ty.ptrInfo(zcu);
26574 const new_ptr_ty = try pt.ptrType(.{
26575 .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(),
26576 .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none,
26577 .flags = .{
26578 .size = .many,
26579 .alignment = ptr_info.flags.alignment,
26580 .is_const = ptr_info.flags.is_const,
26581 .is_volatile = ptr_info.flags.is_volatile,
26582 .is_allowzero = ptr_info.flags.is_allowzero,
26583 .address_space = ptr_info.flags.address_space,
26584 .vector_index = ptr_info.flags.vector_index,
26585 },
26586 .packed_offset = ptr_info.packed_offset,
26587 });
26588 const ptr_ptr_info = object_ptr_ty.ptrInfo(zcu);
26589 const result_ty = try pt.ptrType(.{
26590 .child = new_ptr_ty.toIntern(),
26591 .sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none,
26592 .flags = .{
26593 .size = .one,
26594 .alignment = ptr_ptr_info.flags.alignment,
26595 .is_const = ptr_ptr_info.flags.is_const,
26596 .is_volatile = ptr_ptr_info.flags.is_volatile,
26597 .is_allowzero = ptr_ptr_info.flags.is_allowzero,
26598 .address_space = ptr_ptr_info.flags.address_space,
26599 .vector_index = ptr_ptr_info.flags.vector_index,
26600 },
26601 .packed_offset = ptr_ptr_info.packed_offset,
26602 });
26603 return block.addTyOp(.ptr_cast, result_ty, object_ptr);
26604 } else {
26605 return sema.fail(
26606 block,
26607 field_name_src,
26608 "no member named '{f}' in '{f}'",
26609 .{ field_name.fmt(ip), object_ty.fmt(pt) },
26610 );
26611 }
26612 },
26613 .pointer => if (inner_ty.isSlice(zcu)) {
26614 const inner_ptr = if (is_pointer_to)
26615 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
26616 else
26617 object_ptr;
26618
26619 const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty;
26620
26621 if (field_name.eqlSlice("ptr", ip)) {
26622 const result_ty = try attr_ptr_ty.fieldPtrType(Value.slice_ptr_index, pt);
26623 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
26624 return Air.internedToRef((try val.ptrField(Value.slice_ptr_index, pt)).toIntern());
26625 }
26626 try sema.requireRuntimeBlock(block, src, null);
26627
26628 const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
26629 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
26630 return field_ptr;
26631 } else if (field_name.eqlSlice("len", ip)) {
26632 const result_ty = try attr_ptr_ty.fieldPtrType(Value.slice_len_index, pt);
26633 if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| {
26634 return Air.internedToRef((try val.ptrField(Value.slice_len_index, pt)).toIntern());
26635 }
26636 try sema.requireRuntimeBlock(block, src, null);
26637
26638 const field_ptr = try block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr);
26639 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
26640 return field_ptr;
26641 } else {
26642 return sema.fail(
26643 block,
26644 field_name_src,
26645 "no member named '{f}' in '{f}'",
26646 .{ field_name.fmt(ip), object_ty.fmt(pt) },
26647 );
26648 }
26649 },
26650 .type => {
26651 const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src);
26652 const inner = if (is_pointer_to)
26653 try sema.analyzeLoad(block, src, result, object_ptr_src)
26654 else
26655 result;
26656
26657 const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?;
26658 const child_type = val.toType();
26659
26660 switch (child_type.zigTypeTag(zcu)) {
26661 .error_set => {
26662 const err_set_ty: Type = err_set: switch (ip.indexToKey(child_type.toIntern())) {
26663 .inferred_error_set_type => |func_index| {
26664 try sema.ensureFuncIesResolved(block, src, func_index);
26665 const resolved_ies = ip.funcIesResolvedUnordered(func_index);
26666 continue :err_set ip.indexToKey(resolved_ies);
26667 },
26668 .error_set_type => |err_set| if (err_set.nameIndex(ip, field_name) == null) {
26669 return sema.fail(block, src, "no error named '{f}' in '{f}'", .{
26670 field_name.fmt(ip), child_type.fmt(pt),
26671 });
26672 } else child_type,
26673 .simple_type => |t| {
26674 assert(t == .anyerror);
26675 _ = try pt.getErrorValue(field_name);
26676 break :err_set try pt.singleErrorSetType(field_name);
26677 },
26678 else => unreachable,
26679 };
26680 return uavRef(sema, .fromInterned(try pt.intern(.{ .err = .{
26681 .ty = err_set_ty.toIntern(),
26682 .name = field_name,
26683 } })));
26684 },
26685 .@"union" => {
26686 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
26687 return inst;
26688 }
26689 try sema.ensureLayoutResolved(child_type, src, .field_used);
26690 if (child_type.unionTagType(zcu)) |enum_ty| {
26691 if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| {
26692 const field_index_u32: u32 = @intCast(field_index);
26693 const idx_val = try pt.enumValueFieldIndex(enum_ty, field_index_u32);
26694 return uavRef(sema, idx_val);
26695 }
26696 }
26697 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
26698 },
26699 .@"enum" => {
26700 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
26701 return inst;
26702 }
26703 try sema.ensureLayoutResolved(child_type, src, .field_used);
26704 const field_index = child_type.enumFieldIndex(field_name, zcu) orelse {
26705 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
26706 };
26707 const field_index_u32: u32 = @intCast(field_index);
26708 const idx_val = try pt.enumValueFieldIndex(child_type, field_index_u32);
26709 return uavRef(sema, idx_val);
26710 },
26711 .@"struct", .@"opaque" => {
26712 if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| {
26713 return inst;
26714 }
26715 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
26716 },
26717 else => return sema.fail(block, src, "type '{f}' has no members", .{child_type.fmt(pt)}),
26718 }
26719 },
26720 .@"struct" => {
26721 const inner_ptr = if (is_pointer_to)
26722 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
26723 else
26724 object_ptr;
26725 try sema.ensureLayoutResolved(inner_ty, src, .ptr_access);
26726 const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty);
26727 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
26728 return field_ptr;
26729 },
26730 .@"union" => {
26731 const inner_ptr = if (is_pointer_to)
26732 try sema.analyzeLoad(block, src, object_ptr, object_ptr_src)
26733 else
26734 object_ptr;
26735 try sema.ensureLayoutResolved(inner_ty, src, .ptr_access);
26736 const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing);
26737 try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr);
26738 return field_ptr;
26739 },
26740 else => {},
26741 }
26742 return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name);
26743}
26744
26745const ResolvedFieldCallee = union(enum) {
26746 /// The LHS of the call was an actual field with this value.
26747 direct: Air.Inst.Ref,
26748 /// This is a method call, with the function and first argument given.
26749 method: struct {
26750 func_inst: Air.Inst.Ref,
26751 arg0_inst: Air.Inst.Ref,
26752 },
26753};
26754
26755fn fieldCallBind(
26756 sema: *Sema,
26757 block: *Block,
26758 src: LazySrcLoc,
26759 raw_ptr: Air.Inst.Ref,
26760 field_name: InternPool.NullTerminatedString,
26761 field_name_src: LazySrcLoc,
26762) CompileError!ResolvedFieldCallee {
26763 // When editing this function, note that there is corresponding logic to be edited
26764 // in `fieldVal`. This function takes a pointer and returns a pointer.
26765
26766 const pt = sema.pt;
26767 const zcu = pt.zcu;
26768 const ip = &zcu.intern_pool;
26769 const raw_ptr_src = src; // TODO better source location
26770 const raw_ptr_ty = sema.typeOf(raw_ptr);
26771 const inner_ty = if (raw_ptr_ty.zigTypeTag(zcu) == .pointer and (raw_ptr_ty.ptrSize(zcu) == .one or raw_ptr_ty.ptrSize(zcu) == .c))
26772 raw_ptr_ty.childType(zcu)
26773 else
26774 return sema.fail(block, raw_ptr_src, "expected single pointer, found '{f}'", .{raw_ptr_ty.fmt(pt)});
26775
26776 // Optionally dereference a second pointer to get the concrete type.
26777 const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one;
26778 const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty;
26779 try sema.ensureLayoutResolved(concrete_ty, src, .ptr_access);
26780 const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty;
26781 const object_ptr = if (is_double_ptr)
26782 try sema.analyzeLoad(block, src, raw_ptr, src)
26783 else
26784 raw_ptr;
26785
26786 find_field: {
26787 switch (concrete_ty.zigTypeTag(zcu)) {
26788 .@"struct" => {
26789 if (zcu.typeToStruct(concrete_ty)) |struct_type| {
26790 const field_index = struct_type.nameIndex(ip, field_name) orelse break :find_field;
26791 return sema.finishFieldCallBind(block, src, ptr_ty, field_index, object_ptr);
26792 } else if (concrete_ty.isTuple(zcu)) {
26793 if (field_name.eqlSlice("len", ip)) {
26794 return .{ .direct = try pt.intRef(.usize, concrete_ty.structFieldCount(zcu)) };
26795 }
26796 if (field_name.toUnsigned(ip)) |field_index| {
26797 if (field_index >= concrete_ty.structFieldCount(zcu)) break :find_field;
26798 return sema.finishFieldCallBind(block, src, ptr_ty, field_index, object_ptr);
26799 }
26800 } else {
26801 const max = concrete_ty.structFieldCount(zcu);
26802 for (0..max) |i_usize| {
26803 const i: u32 = @intCast(i_usize);
26804 if (field_name == concrete_ty.structFieldName(i, zcu).unwrap().?) {
26805 return sema.finishFieldCallBind(block, src, ptr_ty, i, object_ptr);
26806 }
26807 }
26808 }
26809 },
26810 .@"union" => {
26811 const union_obj = zcu.typeToUnion(concrete_ty).?;
26812 const enum_obj = ip.loadEnumType(union_obj.enum_tag_type);
26813 if (enum_obj.nameIndex(ip, field_name) == null) break :find_field;
26814 const field_ptr = try unionFieldPtr(sema, block, src, object_ptr, field_name, field_name_src, concrete_ty, false);
26815 return .{ .direct = try sema.analyzeLoad(block, src, field_ptr, src) };
26816 },
26817 .type => {
26818 const namespace = try sema.analyzeLoad(block, src, object_ptr, src);
26819 return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) };
26820 },
26821 else => {},
26822 }
26823 }
26824
26825 // If we get here, we need to look for a decl in the struct type instead.
26826 const found_nav = found_nav: {
26827 const namespace = concrete_ty.getNamespace(zcu).unwrap() orelse
26828 break :found_nav null;
26829 const nav_index = try sema.namespaceLookup(block, src, namespace, field_name) orelse
26830 break :found_nav null;
26831
26832 const decl_val = try sema.analyzeNavVal(block, src, nav_index);
26833 const decl_type = sema.typeOf(decl_val);
26834 if (zcu.typeToFunc(decl_type)) |func_type| f: {
26835 if (func_type.param_types.len == 0) break :f;
26836
26837 const first_param_type: Type = .fromInterned(func_type.param_types.get(ip)[0]);
26838 if (first_param_type.isGenericPoison() or
26839 (first_param_type.zigTypeTag(zcu) == .pointer and
26840 (first_param_type.ptrSize(zcu) == .one or
26841 first_param_type.ptrSize(zcu) == .c) and
26842 first_param_type.childType(zcu).eql(concrete_ty)))
26843 {
26844 // Note that if the param type is generic poison, we know that it must
26845 // specifically be `anytype` since it's the first parameter, meaning we
26846 // can safely assume it can be a pointer.
26847 // TODO: bound fn calls on rvalues should probably
26848 // generate a by-value argument somehow.
26849 return .{ .method = .{
26850 .func_inst = decl_val,
26851 .arg0_inst = object_ptr,
26852 } };
26853 } else if (first_param_type.eql(concrete_ty)) {
26854 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
26855 return .{ .method = .{
26856 .func_inst = decl_val,
26857 .arg0_inst = deref,
26858 } };
26859 } else if (first_param_type.zigTypeTag(zcu) == .optional) {
26860 const child = first_param_type.optionalChild(zcu);
26861 if (child.eql(concrete_ty)) {
26862 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
26863 return .{ .method = .{
26864 .func_inst = decl_val,
26865 .arg0_inst = deref,
26866 } };
26867 } else if (child.zigTypeTag(zcu) == .pointer and
26868 child.ptrSize(zcu) == .one and
26869 child.childType(zcu).eql(concrete_ty))
26870 {
26871 return .{ .method = .{
26872 .func_inst = decl_val,
26873 .arg0_inst = object_ptr,
26874 } };
26875 }
26876 } else if (first_param_type.zigTypeTag(zcu) == .error_union and
26877 first_param_type.errorUnionPayload(zcu).eql(concrete_ty))
26878 {
26879 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
26880 return .{ .method = .{
26881 .func_inst = decl_val,
26882 .arg0_inst = deref,
26883 } };
26884 }
26885 }
26886 break :found_nav nav_index;
26887 };
26888
26889 const msg = msg: {
26890 const msg = try sema.errMsg(src, "no field or member function named '{f}' in '{f}'", .{
26891 field_name.fmt(ip),
26892 concrete_ty.fmt(pt),
26893 });
26894 errdefer msg.destroy(sema.gpa);
26895 try sema.addDeclaredHereNote(msg, concrete_ty);
26896 if (found_nav) |nav_index| {
26897 try sema.errNote(
26898 zcu.navSrcLoc(nav_index),
26899 msg,
26900 "'{f}' is not a member function",
26901 .{field_name.fmt(ip)},
26902 );
26903 }
26904 if (concrete_ty.zigTypeTag(zcu) == .error_union) {
26905 try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{});
26906 }
26907 if (is_double_ptr) {
26908 try sema.errNote(src, msg, "method invocation only supports up to one level of implicit pointer dereferencing", .{});
26909 try sema.errNote(src, msg, "use '.*' to dereference pointer", .{});
26910 }
26911 break :msg msg;
26912 };
26913 return sema.failWithOwnedErrorMsg(block, msg);
26914}
26915
26916fn finishFieldCallBind(
26917 sema: *Sema,
26918 block: *Block,
26919 src: LazySrcLoc,
26920 ptr_ty: Type,
26921 field_index: u32,
26922 object_ptr: Air.Inst.Ref,
26923) CompileError!ResolvedFieldCallee {
26924 const pt = sema.pt;
26925 const zcu = pt.zcu;
26926 const ptr_field_ty = try ptr_ty.fieldPtrType(field_index, pt);
26927
26928 const container_ty = ptr_ty.childType(zcu);
26929 if (container_ty.zigTypeTag(zcu) == .@"struct") {
26930 if (container_ty.structFieldIsComptime(field_index, zcu)) {
26931 const default_val = (try container_ty.structFieldValueComptime(pt, field_index)).?;
26932 return .{ .direct = Air.internedToRef(default_val.toIntern()) };
26933 }
26934 }
26935
26936 if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| {
26937 const ptr_val = try struct_ptr_val.ptrField(field_index, pt);
26938 const pointer = Air.internedToRef(ptr_val.toIntern());
26939 return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) };
26940 }
26941
26942 try sema.requireRuntimeBlock(block, src, null);
26943 const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty);
26944 return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) };
26945}
26946
26947fn namespaceLookup(
26948 sema: *Sema,
26949 block: *Block,
26950 src: LazySrcLoc,
26951 namespace: InternPool.NamespaceIndex,
26952 decl_name: InternPool.NullTerminatedString,
26953) CompileError!?InternPool.Nav.Index {
26954 const pt = sema.pt;
26955 const zcu = pt.zcu;
26956 const gpa = sema.gpa;
26957 if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| {
26958 if (lookup.accessible == .private) {
26959 return sema.failWithOwnedErrorMsg(block, msg: {
26960 const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{
26961 decl_name.fmt(&zcu.intern_pool),
26962 });
26963 errdefer msg.destroy(gpa);
26964 try sema.errNote(zcu.navSrcLoc(lookup.nav), msg, "declared here", .{});
26965 break :msg msg;
26966 });
26967 }
26968 return lookup.nav;
26969 }
26970 return null;
26971}
26972
26973fn namespaceLookupRef(
26974 sema: *Sema,
26975 block: *Block,
26976 src: LazySrcLoc,
26977 namespace: InternPool.NamespaceIndex,
26978 decl_name: InternPool.NullTerminatedString,
26979) CompileError!?Air.Inst.Ref {
26980 const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null;
26981 return try sema.analyzeNavRef(block, src, nav);
26982}
26983
26984fn namespaceLookupVal(
26985 sema: *Sema,
26986 block: *Block,
26987 src: LazySrcLoc,
26988 namespace: InternPool.NamespaceIndex,
26989 decl_name: InternPool.NullTerminatedString,
26990) CompileError!?Air.Inst.Ref {
26991 const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null;
26992 return try sema.analyzeNavVal(block, src, nav);
26993}
26994
26995/// Asserts that the layout of `struct_ty` is already resolved.
26996fn structFieldPtr(
26997 sema: *Sema,
26998 block: *Block,
26999 src: LazySrcLoc,
27000 struct_ptr: Air.Inst.Ref,
27001 field_name: InternPool.NullTerminatedString,
27002 field_name_src: LazySrcLoc,
27003 struct_ty: Type,
27004) CompileError!Air.Inst.Ref {
27005 const pt = sema.pt;
27006 const zcu = pt.zcu;
27007 const ip = &zcu.intern_pool;
27008
27009 assert(struct_ty.zigTypeTag(zcu) == .@"struct");
27010 struct_ty.assertHasLayout(zcu);
27011
27012 const field_index: u32 = if (struct_ty.isTuple(zcu)) field_index: {
27013 if (field_name.eqlSlice("len", ip)) {
27014 const len_inst = try pt.intRef(.usize, struct_ty.structFieldCount(zcu));
27015 return sema.analyzeRef(block, src, len_inst, .none);
27016 }
27017 break :field_index try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src);
27018 } else field_index: {
27019 const struct_type = zcu.typeToStruct(struct_ty).?;
27020 break :field_index struct_type.nameIndex(ip, field_name) orelse {
27021 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
27022 };
27023 };
27024
27025 return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty);
27026}
27027
27028/// Supports both structs and unions.
27029///
27030/// Asserts that the layout of `struct_ty` is already resolved.
27031fn structFieldPtrByIndex(
27032 sema: *Sema,
27033 block: *Block,
27034 src: LazySrcLoc,
27035 struct_ptr: Air.Inst.Ref,
27036 field_index: u32,
27037 struct_ty: Type,
27038) CompileError!Air.Inst.Ref {
27039 const pt = sema.pt;
27040 const zcu = pt.zcu;
27041
27042 struct_ty.assertHasLayout(zcu);
27043 const struct_ptr_ty = sema.typeOf(struct_ptr);
27044
27045 if (struct_ty.structFieldIsComptime(field_index, zcu)) {
27046 const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt);
27047 return .fromIntern(try pt.intern(.{ .ptr = .{
27048 .ty = field_ptr_ty.toIntern(),
27049 .base_addr = .{ .comptime_field = struct_ty.structFieldDefaultValue(field_index, zcu).?.toIntern() },
27050 .byte_offset = 0,
27051 } }));
27052 } else if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| {
27053 return .fromValue(try struct_ptr_val.ptrField(field_index, pt));
27054 } else {
27055 const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt);
27056 return block.addStructFieldPtr(struct_ptr, field_index, field_ptr_ty);
27057 }
27058}
27059
27060fn structFieldVal(
27061 sema: *Sema,
27062 block: *Block,
27063 struct_byval: Air.Inst.Ref,
27064 field_name: InternPool.NullTerminatedString,
27065 field_name_src: LazySrcLoc,
27066 struct_ty: Type,
27067) CompileError!Air.Inst.Ref {
27068 const pt = sema.pt;
27069 const zcu = pt.zcu;
27070 const ip = &zcu.intern_pool;
27071 assert(struct_ty.zigTypeTag(zcu) == .@"struct");
27072 assert(sema.typeOf(struct_byval).toIntern() == struct_ty.toIntern());
27073 struct_ty.assertHasLayout(zcu);
27074
27075 switch (ip.indexToKey(struct_ty.toIntern())) {
27076 .struct_type => {
27077 const struct_type = ip.loadStructType(struct_ty.toIntern());
27078
27079 const field_index = struct_type.nameIndex(ip, field_name) orelse
27080 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name);
27081 if (struct_type.field_is_comptime_bits.get(ip, field_index)) {
27082 return .fromIntern(struct_type.field_defaults.get(ip)[field_index]);
27083 }
27084
27085 const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]);
27086 if (try field_ty.onePossibleValue(pt)) |field_val|
27087 return .fromValue(field_val);
27088
27089 if (sema.resolveValue(struct_byval)) |struct_val| {
27090 if (struct_val.isUndef(zcu)) return pt.undefRef(field_ty);
27091 return .fromValue(try struct_val.fieldValue(pt, field_index));
27092 }
27093
27094 return block.addStructFieldVal(struct_byval, field_index, field_ty);
27095 },
27096 .tuple_type => {
27097 return sema.tupleFieldVal(block, struct_byval, field_name, field_name_src, struct_ty);
27098 },
27099 else => unreachable,
27100 }
27101}
27102
27103fn tupleFieldVal(
27104 sema: *Sema,
27105 block: *Block,
27106 tuple_byval: Air.Inst.Ref,
27107 field_name: InternPool.NullTerminatedString,
27108 field_name_src: LazySrcLoc,
27109 tuple_ty: Type,
27110) CompileError!Air.Inst.Ref {
27111 const pt = sema.pt;
27112 const zcu = pt.zcu;
27113 if (field_name.eqlSlice("len", &zcu.intern_pool)) {
27114 return pt.intRef(.usize, tuple_ty.structFieldCount(zcu));
27115 }
27116 const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src);
27117 return sema.tupleFieldValByIndex(block, tuple_byval, field_index, tuple_ty);
27118}
27119
27120/// Asserts that `field_name` is not "len".
27121fn tupleFieldIndex(
27122 sema: *Sema,
27123 block: *Block,
27124 tuple_ty: Type,
27125 field_name: InternPool.NullTerminatedString,
27126 field_name_src: LazySrcLoc,
27127) CompileError!u32 {
27128 const pt = sema.pt;
27129 const ip = &pt.zcu.intern_pool;
27130 assert(!field_name.eqlSlice("len", ip));
27131 if (field_name.toUnsigned(ip)) |field_index| {
27132 if (field_index < tuple_ty.structFieldCount(pt.zcu)) return field_index;
27133 return sema.fail(block, field_name_src, "index '{f}' out of bounds of tuple '{f}'", .{
27134 field_name.fmt(ip), tuple_ty.fmt(pt),
27135 });
27136 }
27137
27138 return sema.fail(block, field_name_src, "no field named '{f}' in tuple '{f}'", .{
27139 field_name.fmt(ip), tuple_ty.fmt(pt),
27140 });
27141}
27142
27143fn tupleFieldValByIndex(
27144 sema: *Sema,
27145 block: *Block,
27146 tuple_byval: Air.Inst.Ref,
27147 field_index: u32,
27148 tuple_ty: Type,
27149) CompileError!Air.Inst.Ref {
27150 const pt = sema.pt;
27151 const zcu = pt.zcu;
27152 const field_ty = tuple_ty.fieldType(field_index, zcu);
27153
27154 if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| {
27155 return Air.internedToRef(default_value.toIntern());
27156 }
27157
27158 if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
27159
27160 if (sema.resolveValue(tuple_byval)) |tuple_val| {
27161 return switch (zcu.intern_pool.indexToKey(tuple_val.toIntern())) {
27162 .undef => pt.undefRef(field_ty),
27163 .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) {
27164 .bytes => |bytes| try pt.intValue(.u8, bytes.at(field_index, &zcu.intern_pool)),
27165 .elems => |elems| Value.fromInterned(elems[field_index]),
27166 .repeated_elem => |elem| Value.fromInterned(elem),
27167 }.toIntern()),
27168 else => unreachable,
27169 };
27170 }
27171
27172 return block.addStructFieldVal(tuple_byval, field_index, field_ty);
27173}
27174
27175/// Asserts that the layout of `union_ty` is already resolved.
27176fn unionFieldPtr(
27177 sema: *Sema,
27178 block: *Block,
27179 src: LazySrcLoc,
27180 union_ptr: Air.Inst.Ref,
27181 field_name: InternPool.NullTerminatedString,
27182 field_name_src: LazySrcLoc,
27183 union_ty: Type,
27184 initializing: bool,
27185) CompileError!Air.Inst.Ref {
27186 const pt = sema.pt;
27187 const zcu = pt.zcu;
27188 const ip = &zcu.intern_pool;
27189
27190 assert(union_ty.zigTypeTag(zcu) == .@"union");
27191 union_ty.assertHasLayout(zcu);
27192
27193 const union_obj = zcu.typeToUnion(union_ty).?;
27194 const tag_ty: Type = .fromInterned(union_obj.enum_tag_type);
27195
27196 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
27197 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
27198
27199 if (initializing and field_ty.classify(zcu) == .no_possible_value) {
27200 const msg = msg: {
27201 const msg = try sema.errMsg(src, "cannot initialize union field with uninstantiable type '{f}'", .{field_ty.fmt(pt)});
27202 errdefer msg.destroy(sema.gpa);
27203
27204 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
27205 field_name.fmt(ip),
27206 });
27207 try sema.addDeclaredHereNote(msg, union_ty);
27208 break :msg msg;
27209 };
27210 return sema.failWithOwnedErrorMsg(block, msg);
27211 }
27212
27213 if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: {
27214 switch (union_obj.layout) {
27215 .auto => if (initializing) {
27216 if (!sema.isComptimeMutablePtr(union_ptr_val)) {
27217 // The initialization is a runtime operation.
27218 break :ct;
27219 }
27220 // Store to the union to initialize the tag.
27221 const field_tag = try pt.enumValueFieldIndex(tag_ty, field_index);
27222 const payload_val = try field_ty.onePossibleValue(pt) orelse try pt.undefValue(field_ty);
27223 const new_union_val = try pt.unionValue(union_ty, field_tag, payload_val);
27224 try sema.storePtrVal(block, src, union_ptr_val, new_union_val, union_ty);
27225 } else {
27226 const union_val = try sema.pointerDeref(block, src, union_ptr_val, union_ptr_val.typeOf(zcu)) orelse break :ct;
27227 if (union_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null);
27228 const active_index = tag_ty.enumTagFieldIndex(union_val.unionTag(zcu).?, zcu).?;
27229 if (active_index != field_index) {
27230 const msg = msg: {
27231 const active_field_name = tag_ty.enumFieldName(active_index, zcu);
27232 const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
27233 field_name.fmt(ip),
27234 active_field_name.fmt(ip),
27235 });
27236 errdefer msg.destroy(sema.gpa);
27237 try sema.addDeclaredHereNote(msg, union_ty);
27238 break :msg msg;
27239 };
27240 return sema.failWithOwnedErrorMsg(block, msg);
27241 }
27242 },
27243 .@"packed", .@"extern" => {},
27244 }
27245 return .fromValue(try union_ptr_val.ptrField(field_index, pt));
27246 }
27247
27248 // If the union has a tag, we must either set or or safety check it depending on `initializing`.
27249 tag: {
27250 if (union_ty.containerLayout(zcu) != .auto) break :tag;
27251 if (tag_ty.classify(zcu) == .one_possible_value) break :tag;
27252 // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but
27253 // only emit a safety check if it's available at runtime (i.e. it's safety-tagged).
27254 const want_tag = try pt.enumValueFieldIndex(tag_ty, field_index);
27255 if (initializing) {
27256 const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag));
27257 try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store
27258 } else if (block.wantSafety() and union_obj.has_runtime_tag) {
27259 // The tag exists at runtime (actual or safety tag), so emit a safety check.
27260 // TODO would it be better if get_union_tag supported pointers to unions?
27261 const union_val = try block.addTyOp(.load, union_ty, union_ptr);
27262 const active_tag = try block.addTyOp(.get_union_tag, tag_ty, union_val);
27263 try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(want_tag));
27264 }
27265 }
27266 if (field_ty.classify(zcu) == .no_possible_value) {
27267 _ = try block.addNoOp(.unreach);
27268 return .unreachable_value;
27269 }
27270
27271 const field_ptr_ty = try sema.typeOf(union_ptr).fieldPtrType(field_index, pt);
27272 return block.addStructFieldPtr(union_ptr, field_index, field_ptr_ty);
27273}
27274
27275fn unionFieldVal(
27276 sema: *Sema,
27277 block: *Block,
27278 src: LazySrcLoc,
27279 union_byval: Air.Inst.Ref,
27280 field_name: InternPool.NullTerminatedString,
27281 field_name_src: LazySrcLoc,
27282 union_ty: Type,
27283) CompileError!Air.Inst.Ref {
27284 const pt = sema.pt;
27285 const zcu = pt.zcu;
27286 const ip = &zcu.intern_pool;
27287 assert(union_ty.zigTypeTag(zcu) == .@"union");
27288 assert(sema.typeOf(union_byval).toIntern() == union_ty.toIntern());
27289 union_ty.assertHasLayout(zcu);
27290
27291 const union_obj = zcu.typeToUnion(union_ty).?;
27292 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
27293 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
27294 const enum_tag_ty: Type = .fromInterned(union_obj.enum_tag_type);
27295
27296 if (sema.resolveValue(union_byval)) |union_val| {
27297 if (union_val.isUndef(zcu)) return pt.undefRef(field_ty);
27298 switch (union_obj.layout) {
27299 .auto => {
27300 const active_tag_val = union_val.unionTag(zcu).?;
27301 const active_index = enum_tag_ty.enumTagFieldIndex(active_tag_val, zcu).?;
27302 if (active_index == field_index) return .fromValue(union_val.unionPayload(zcu));
27303 return sema.failWithOwnedErrorMsg(block, msg: {
27304 const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{
27305 field_name.fmt(ip), enum_tag_ty.enumFieldName(active_index, zcu).fmt(ip),
27306 });
27307 errdefer msg.destroy(zcu.comp.gpa);
27308 try sema.addDeclaredHereNote(msg, union_ty);
27309 break :msg msg;
27310 });
27311 },
27312 .@"extern" => if (try sema.castMemory(union_val, field_ty, 0)) |field_val| {
27313 return .fromValue(field_val);
27314 } else {
27315 // Runtime-known due to a pointer-to-integer conversion.
27316 },
27317 .@"packed" => {
27318 return .fromValue(try sema.bitCastVal(union_val, field_ty));
27319 },
27320 }
27321 }
27322
27323 if (union_obj.layout == .auto and block.wantSafety() and union_obj.has_runtime_tag) {
27324 const wanted_tag_val = try pt.enumValueFieldIndex(enum_tag_ty, field_index);
27325 const active_tag = try block.addTyOp(.get_union_tag, enum_tag_ty, union_byval);
27326 try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(wanted_tag_val));
27327 }
27328
27329 if (field_ty.classify(zcu) == .no_possible_value) {
27330 _ = try block.addNoOp(.unreach);
27331 return .unreachable_value;
27332 }
27333
27334 if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
27335
27336 return block.addStructFieldVal(union_byval, field_index, field_ty);
27337}
27338
27339fn elemPtr(
27340 sema: *Sema,
27341 block: *Block,
27342 src: LazySrcLoc,
27343 indexable_ptr: Air.Inst.Ref,
27344 elem_index: Air.Inst.Ref,
27345 elem_index_src: LazySrcLoc,
27346 init: bool,
27347 oob_safety: bool,
27348) CompileError!Air.Inst.Ref {
27349 const pt = sema.pt;
27350 const zcu = pt.zcu;
27351 const indexable_ptr_src = src; // TODO better source location
27352 const indexable_ptr_ty = sema.typeOf(indexable_ptr);
27353
27354 const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(zcu)) {
27355 .pointer => indexable_ptr_ty.childType(zcu),
27356 else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}),
27357 };
27358 try sema.checkIndexable(block, src, indexable_ty);
27359 try sema.ensureLayoutResolved(indexable_ty, src, .ptr_access);
27360
27361 const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) {
27362 .vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init),
27363 .array => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety),
27364 .@"struct" => try sema.tupleElemPtr(block, src, indexable_ptr, elem_index, elem_index_src),
27365 .spirv => try sema.elemPtrSpirvRuntimeArray(block, indexable_ptr, elem_index),
27366 else => {
27367 const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src);
27368 try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src, .ptr_access);
27369 return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety);
27370 },
27371 };
27372
27373 try sema.checkKnownAllocPtr(block, indexable_ptr, elem_ptr);
27374 return elem_ptr;
27375}
27376
27377/// Asserts that `indexable` is an indexable pointer whose child type has its layout already resolved.
27378fn elemPtrOneLayerOnly(
27379 sema: *Sema,
27380 block: *Block,
27381 src: LazySrcLoc,
27382 indexable: Air.Inst.Ref,
27383 elem_index: Air.Inst.Ref,
27384 elem_index_src: LazySrcLoc,
27385 init: bool,
27386 oob_safety: bool,
27387) CompileError!Air.Inst.Ref {
27388 const indexable_src = src; // TODO better source location
27389 const indexable_ty = sema.typeOf(indexable);
27390 const pt = sema.pt;
27391 const zcu = pt.zcu;
27392
27393 assert(indexable_ty.isIndexable(zcu));
27394 assert(indexable_ty.zigTypeTag(zcu) == .pointer);
27395 const child_ty = indexable_ty.childType(zcu);
27396 child_ty.assertHasLayout(zcu);
27397
27398 switch (indexable_ty.ptrSize(zcu)) {
27399 .slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
27400 .many, .c => {
27401 const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
27402 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
27403 const maybe_index: ?u64 = if (maybe_index_val) |val| val.toUnsignedInt(zcu) else null;
27404 ct: {
27405 const ptr_val = maybe_ptr_val orelse break :ct;
27406 const index: usize = @intCast(maybe_index orelse break :ct);
27407 return .fromValue(try ptr_val.ptrElem(index, pt));
27408 }
27409
27410 const result_ty = try indexable_ty.elemPtrType(maybe_index, pt);
27411
27412 try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_src);
27413 try sema.validateRuntimeValue(block, indexable_src, indexable);
27414 try sema.checkLogicalPtrOperation(block, src, indexable_ty);
27415
27416 if (child_ty.abiSize(zcu) == 0) {
27417 // zero-bit child type; just bitcast the pointer
27418 return block.addTyOp(.ptr_cast, result_ty, indexable);
27419 }
27420
27421 return block.addPtrElemPtr(indexable, elem_index, result_ty);
27422 },
27423 .one => {
27424 const elem_ptr = switch (child_ty.zigTypeTag(zcu)) {
27425 .vector => try sema.elemPtrVector(block, indexable_src, indexable, elem_index_src, elem_index, init),
27426 .array => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety),
27427 .@"struct" => try sema.tupleElemPtr(block, indexable_src, indexable, elem_index, elem_index_src),
27428 .spirv => try sema.elemPtrSpirvRuntimeArray(block, indexable, elem_index),
27429 else => unreachable, // Guaranteed by checkIndexable
27430 };
27431 try sema.checkKnownAllocPtr(block, indexable, elem_ptr);
27432 return elem_ptr;
27433 },
27434 }
27435}
27436
27437fn elemVal(
27438 sema: *Sema,
27439 block: *Block,
27440 src: LazySrcLoc,
27441 indexable: Air.Inst.Ref,
27442 elem_index_uncasted: Air.Inst.Ref,
27443 elem_index_src: LazySrcLoc,
27444 oob_safety: bool,
27445) CompileError!Air.Inst.Ref {
27446 const indexable_src = src; // TODO better source location
27447 const indexable_ty = sema.typeOf(indexable);
27448 const pt = sema.pt;
27449 const zcu = pt.zcu;
27450
27451 try sema.checkIndexable(block, src, indexable_ty);
27452
27453 // TODO in case of a vector of pointers, we need to detect whether the element
27454 // index is a scalar or vector instead of unconditionally casting to usize.
27455 const elem_index = try sema.coerce(block, .usize, elem_index_uncasted, elem_index_src);
27456
27457 switch (indexable_ty.zigTypeTag(zcu)) {
27458 .pointer => {
27459 const child_ty = indexable_ty.childType(zcu);
27460 try sema.ensureLayoutResolved(child_ty, src, .ptr_access);
27461 switch (indexable_ty.ptrSize(zcu)) {
27462 .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
27463 .many, .c => {
27464 const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable);
27465 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
27466
27467 ct: {
27468 const indexable_val = maybe_indexable_val orelse break :ct;
27469 const index_val = maybe_index_val orelse break :ct;
27470 const index: usize = @intCast(index_val.toUnsignedInt(zcu));
27471 const many_ptr_ty = try pt.manyConstPtrType(child_ty);
27472 const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty);
27473 const elem_ptr_val = try many_ptr_val.ptrElem(index, pt);
27474 return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), indexable_src);
27475 }
27476
27477 try sema.validateRuntimeElemAccess(block, elem_index_src, child_ty, src);
27478 switch (child_ty.classify(zcu)) {
27479 .runtime => {},
27480 .one_possible_value => return .fromValue((try child_ty.onePossibleValue(pt)).?),
27481 .no_possible_value => switch (child_ty.zigTypeTag(zcu)) {
27482 .@"opaque" => return sema.fail(block, src, "cannot load opaque type '{f}'", .{child_ty.fmt(pt)}),
27483 else => return sema.fail(block, src, "cannot load uninstantiable type '{f}'", .{child_ty.fmt(pt)}),
27484 },
27485 .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess`
27486 }
27487 try sema.checkLogicalPtrOperation(block, src, indexable_ty);
27488
27489 return block.addBinOp(.ptr_elem_val, indexable, elem_index);
27490 },
27491 .one => {
27492 arr_sent: {
27493 if (child_ty.zigTypeTag(zcu) != .array) break :arr_sent;
27494 const sentinel = child_ty.sentinel(zcu) orelse break :arr_sent;
27495 const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
27496 const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(zcu));
27497 if (index != child_ty.arrayLen(zcu)) break :arr_sent;
27498 return .fromValue(sentinel);
27499 }
27500 const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
27501 return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
27502 },
27503 }
27504 },
27505 .array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety),
27506 .vector => {
27507 // TODO: If the index is a vector, the result should be a vector.
27508 return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety);
27509 },
27510 .@"struct" => {
27511 // Tuple field access.
27512 const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
27513 const index: u32 = @intCast(index_val.toUnsignedInt(zcu));
27514 return sema.tupleField(block, indexable_src, indexable, elem_index_src, index);
27515 },
27516 else => unreachable,
27517 }
27518}
27519
27520/// Called when the index or indexable is runtime known.
27521/// Asserts that the layout of `elem_ty` is already resolved.
27522fn validateRuntimeElemAccess(
27523 sema: *Sema,
27524 block: *Block,
27525 elem_index_src: LazySrcLoc,
27526 elem_ty: Type,
27527 parent_src: LazySrcLoc,
27528) CompileError!void {
27529 const zcu = sema.pt.zcu;
27530
27531 if (elem_ty.comptimeOnly(zcu)) {
27532 const msg = msg: {
27533 const msg = try sema.errMsg(
27534 elem_index_src,
27535 "values of type '{f}' must be comptime-known, but index value is runtime-known",
27536 .{elem_ty.fmt(sema.pt)},
27537 );
27538 errdefer msg.destroy(sema.gpa);
27539
27540 try sema.explainWhyTypeIsComptime(msg, parent_src, elem_ty);
27541
27542 break :msg msg;
27543 };
27544 return sema.failWithOwnedErrorMsg(block, msg);
27545 }
27546}
27547
27548/// Validates `elem_index`, and returns a pointer to that field using `structFieldPtrByIndex`.
27549///
27550/// Asserts that the type of `tuple_ptr` is a single-item pointer whose child type is a tuple.
27551fn tupleElemPtr(
27552 sema: *Sema,
27553 block: *Block,
27554 src: LazySrcLoc,
27555 tuple_ptr: Air.Inst.Ref,
27556 elem_index: Air.Inst.Ref,
27557 elem_index_src: LazySrcLoc,
27558) CompileError!Air.Inst.Ref {
27559 const pt = sema.pt;
27560 const zcu = pt.zcu;
27561 const tuple_ptr_ty = sema.typeOf(tuple_ptr);
27562 assert(tuple_ptr_ty.isSinglePointer(zcu));
27563 const tuple_ty = tuple_ptr_ty.childType(zcu);
27564 assert(tuple_ty.isTuple(zcu));
27565
27566 const field_count = tuple_ty.structFieldCount(zcu);
27567 if (field_count == 0) {
27568 return sema.fail(block, src, "indexing into empty tuple is not allowed", .{});
27569 }
27570
27571 const elem_index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index });
27572 const index = elem_index_val.getUnsignedInt(zcu);
27573 if (index == null or index.? >= field_count) {
27574 return sema.fail(block, elem_index_src, "index '{f}' out of bounds of tuple '{f}'", .{
27575 elem_index_val.fmtValueSema(pt, sema), tuple_ty.fmt(pt),
27576 });
27577 }
27578
27579 return sema.structFieldPtrByIndex(block, src, tuple_ptr, @intCast(index.?), tuple_ty);
27580}
27581
27582fn tupleField(
27583 sema: *Sema,
27584 block: *Block,
27585 tuple_src: LazySrcLoc,
27586 tuple: Air.Inst.Ref,
27587 field_index_src: LazySrcLoc,
27588 field_index: u32,
27589) CompileError!Air.Inst.Ref {
27590 const pt = sema.pt;
27591 const zcu = pt.zcu;
27592 const tuple_ty = sema.typeOf(tuple);
27593 const field_count = tuple_ty.structFieldCount(zcu);
27594
27595 if (field_count == 0) {
27596 return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{});
27597 }
27598
27599 if (field_index >= field_count) {
27600 return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{
27601 field_index, field_count,
27602 });
27603 }
27604
27605 const field_ty = tuple_ty.fieldType(field_index, zcu);
27606
27607 if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| {
27608 return Air.internedToRef(default_value.toIntern()); // comptime field
27609 }
27610
27611 if (sema.resolveValue(tuple)) |tuple_val| {
27612 if (tuple_val.isUndef(zcu)) return pt.undefRef(field_ty);
27613 return Air.internedToRef((try tuple_val.fieldValue(pt, field_index)).toIntern());
27614 }
27615
27616 try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_src);
27617
27618 return block.addStructFieldVal(tuple, field_index, field_ty);
27619}
27620
27621fn elemValArray(
27622 sema: *Sema,
27623 block: *Block,
27624 src: LazySrcLoc,
27625 array_src: LazySrcLoc,
27626 array: Air.Inst.Ref,
27627 elem_index_src: LazySrcLoc,
27628 elem_index: Air.Inst.Ref,
27629 oob_safety: bool,
27630) CompileError!Air.Inst.Ref {
27631 const pt = sema.pt;
27632 const zcu = pt.zcu;
27633 const array_ty = sema.typeOf(array);
27634 const array_sent = array_ty.sentinel(zcu);
27635 const array_len = array_ty.arrayLen(zcu);
27636 const array_len_s = array_len + @intFromBool(array_sent != null);
27637 const elem_ty = array_ty.childType(zcu);
27638
27639 if (array_len_s == 0) {
27640 return sema.fail(block, array_src, "indexing into empty array is not allowed", .{});
27641 }
27642
27643 const maybe_undef_array_val = sema.resolveValue(array);
27644 // index must be defined since it can access out of bounds
27645 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
27646
27647 if (maybe_index_val) |index_val| {
27648 const index: usize = @intCast(index_val.toUnsignedInt(zcu));
27649 if (array_sent) |s| {
27650 if (index == array_len) {
27651 return Air.internedToRef(s.toIntern());
27652 }
27653 }
27654 if (index >= array_len_s) {
27655 const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else "";
27656 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
27657 }
27658 }
27659 if (maybe_undef_array_val) |array_val| {
27660 if (array_val.isUndef(zcu)) {
27661 return pt.undefRef(elem_ty);
27662 }
27663 if (maybe_index_val) |index_val| {
27664 const index: usize = @intCast(index_val.toUnsignedInt(zcu));
27665 return .fromValue(try array_val.elemValue(pt, index));
27666 }
27667 // Since the array is comptime-known, it might be OPV, in which case the index is irrelevant.
27668 if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
27669 }
27670
27671 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_src);
27672 try sema.validateRuntimeValue(block, array_src, array);
27673
27674 if (oob_safety and block.wantSafety()) {
27675 // Runtime check is only needed if unable to comptime check.
27676 if (maybe_index_val == null) {
27677 const len_inst = try pt.intRef(.usize, array_len);
27678 const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
27679 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
27680 }
27681 }
27682
27683 return block.addBinOp(.array_elem_val, array, elem_index);
27684}
27685
27686fn elemPtrVector(
27687 sema: *Sema,
27688 block: *Block,
27689 vector_ptr_src: LazySrcLoc,
27690 vector_ptr: Air.Inst.Ref,
27691 elem_index_src: LazySrcLoc,
27692 elem_index: Air.Inst.Ref,
27693 init: bool,
27694) CompileError!Air.Inst.Ref {
27695 const pt = sema.pt;
27696 const zcu = pt.zcu;
27697 const vector_ptr_ty = sema.typeOf(vector_ptr);
27698 const vector_ty = vector_ptr_ty.childType(zcu);
27699 assert(vector_ty.zigTypeTag(zcu) == .vector);
27700 const vector_len = vector_ty.vectorLen(zcu);
27701
27702 if (vector_len == 0) {
27703 return sema.fail(block, vector_ptr_src, "cannot index into empty vector", .{});
27704 }
27705
27706 const maybe_vector_ptr_val = sema.resolveValue(vector_ptr);
27707 // The index must not be undefined since it can be out of bounds.
27708 const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse {
27709 return sema.fail(block, elem_index_src, "vector index not comptime known", .{});
27710 };
27711 const index = index_val.toUnsignedInt(zcu);
27712 if (index >= vector_len) {
27713 return sema.fail(block, elem_index_src, "index {d} outside vector of length {d}", .{ index, vector_len });
27714 }
27715
27716 const elem_ty = vector_ty.childType(zcu);
27717
27718 const vector_ptr_info = vector_ptr_ty.ptrInfo(zcu);
27719 const elem_ptr_ty = try pt.ptrType(.{
27720 .child = elem_ty.toIntern(),
27721 .flags = .{
27722 .size = .one,
27723 .alignment = vector_ptr_info.flags.alignment,
27724 .is_const = vector_ptr_info.flags.is_const,
27725 .is_volatile = vector_ptr_info.flags.is_volatile,
27726 .is_allowzero = vector_ptr_info.flags.is_allowzero,
27727 .address_space = vector_ptr_info.flags.address_space,
27728 .vector_index = @fromBackingInt(@intCast(index)),
27729 },
27730 .packed_offset = .{
27731 .host_size = @intCast(vector_len),
27732 .bit_offset = 0,
27733 },
27734 });
27735
27736 if (maybe_vector_ptr_val) |ptr_val| {
27737 if (ptr_val.isUndef(zcu)) return pt.undefRef(elem_ptr_ty);
27738 return .fromValue(try pt.getCoerced(ptr_val, elem_ptr_ty));
27739 }
27740
27741 if (!init) {
27742 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, vector_ptr_src);
27743 try sema.validateRuntimeValue(block, vector_ptr_src, vector_ptr);
27744 }
27745
27746 return block.addTyOp(.ptr_cast, elem_ptr_ty, vector_ptr);
27747}
27748
27749fn elemPtrSpirvRuntimeArray(
27750 sema: *Sema,
27751 block: *Block,
27752 array_ptr: Air.Inst.Ref,
27753 elem_index: Air.Inst.Ref,
27754) CompileError!Air.Inst.Ref {
27755 const pt = sema.pt;
27756 const zcu = pt.zcu;
27757 const array_ptr_ty = sema.typeOf(array_ptr);
27758 assert(array_ptr_ty.ptrSize(zcu) == .one);
27759 const array_ty = array_ptr_ty.childType(zcu);
27760 assert(array_ty.isSpirvRuntimeArray(zcu));
27761 const elem_ptr_ty = try array_ptr_ty.elemPtrType(null, pt);
27762 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
27763}
27764
27765/// Asserts that the layout of the array is already resolved.
27766fn elemPtrArray(
27767 sema: *Sema,
27768 block: *Block,
27769 src: LazySrcLoc,
27770 array_ptr_src: LazySrcLoc,
27771 array_ptr: Air.Inst.Ref,
27772 elem_index_src: LazySrcLoc,
27773 elem_index: Air.Inst.Ref,
27774 init: bool,
27775 oob_safety: bool,
27776) CompileError!Air.Inst.Ref {
27777 const pt = sema.pt;
27778 const zcu = pt.zcu;
27779 const array_ptr_ty = sema.typeOf(array_ptr);
27780 assert(array_ptr_ty.ptrSize(zcu) == .one);
27781 const array_ty = array_ptr_ty.childType(zcu);
27782 assert(array_ty.zigTypeTag(zcu) == .array);
27783 const array_sent = array_ty.sentinel(zcu) != null;
27784 const array_len = array_ty.arrayLen(zcu);
27785 const array_len_s = array_len + @intFromBool(array_sent);
27786
27787 if (array_len_s == 0) {
27788 return sema.fail(block, array_ptr_src, "cannot index into empty array", .{});
27789 }
27790
27791 const maybe_undef_array_ptr_val = sema.resolveValue(array_ptr);
27792 // The index must not be undefined since it can be out of bounds.
27793 const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
27794 const index = index_val.toUnsignedInt(zcu);
27795 if (index >= array_len_s) {
27796 const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else "";
27797 return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label });
27798 }
27799 break :o index;
27800 } else null;
27801
27802 array_ty.assertHasLayout(zcu);
27803 const elem_ptr_ty = try array_ptr_ty.elemPtrType(maybe_index, pt);
27804
27805 if (maybe_undef_array_ptr_val) |array_ptr_val| {
27806 if (array_ptr_val.isUndef(zcu)) {
27807 return pt.undefRef(elem_ptr_ty);
27808 }
27809 if (maybe_index) |index| {
27810 return .fromValue(try array_ptr_val.ptrElem(index, pt));
27811 }
27812 }
27813
27814 if (!init) {
27815 try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.childType(zcu), array_ptr_src);
27816 try sema.validateRuntimeValue(block, array_ptr_src, array_ptr);
27817 }
27818
27819 // Runtime check is only needed if unable to comptime check.
27820 if (oob_safety and block.wantSafety() and maybe_index == null) {
27821 const len_inst = try pt.intRef(.usize, array_len);
27822 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
27823 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
27824 }
27825
27826 if (array_ty.childType(zcu).abiSize(zcu) == 0) {
27827 // zero-bit child type; just bitcast the pointer
27828 return block.addTyOp(.ptr_cast, elem_ptr_ty, array_ptr);
27829 }
27830
27831 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
27832}
27833
27834/// Asserts that the layout of the slice element type is already resolved.
27835fn elemValSlice(
27836 sema: *Sema,
27837 block: *Block,
27838 src: LazySrcLoc,
27839 slice_src: LazySrcLoc,
27840 slice: Air.Inst.Ref,
27841 elem_index_src: LazySrcLoc,
27842 elem_index: Air.Inst.Ref,
27843 oob_safety: bool,
27844) CompileError!Air.Inst.Ref {
27845 const pt = sema.pt;
27846 const zcu = pt.zcu;
27847 const slice_ty = sema.typeOf(slice);
27848 assert(slice_ty.isSlice(zcu));
27849 const slice_sent = slice_ty.sentinel(zcu) != null;
27850 const elem_ty = slice_ty.childType(zcu);
27851
27852 elem_ty.assertHasLayout(zcu);
27853
27854 // slice must be defined since it can dereferenced as null
27855 const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice);
27856 // index must be defined since it can index out of bounds
27857 const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index);
27858
27859 if (maybe_slice_val) |slice_val| {
27860 const slice_len = slice_val.sliceLen(zcu);
27861 const slice_len_s = slice_len + @intFromBool(slice_sent);
27862 if (slice_len_s == 0) {
27863 return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
27864 }
27865 if (maybe_index_val) |index_val| {
27866 const index: usize = @intCast(index_val.toUnsignedInt(zcu));
27867 if (index >= slice_len_s) {
27868 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
27869 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
27870 }
27871 const elem_ptr_val = try slice_val.ptrElem(index, pt);
27872 return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), slice_src);
27873 }
27874 }
27875
27876 if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv);
27877
27878 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_src);
27879 try sema.validateRuntimeValue(block, slice_src, slice);
27880
27881 if (oob_safety and block.wantSafety()) {
27882 const len_inst = if (maybe_slice_val) |slice_val|
27883 try pt.intRef(.usize, slice_val.sliceLen(zcu))
27884 else
27885 try block.addTyOp(.slice_len, .usize, slice);
27886 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
27887 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
27888 }
27889 try sema.checkLogicalPtrOperation(block, src, slice_ty);
27890 return block.addBinOp(.slice_elem_val, slice, elem_index);
27891}
27892
27893/// Asserts that the layout of the slice element type is already resolved.
27894fn elemPtrSlice(
27895 sema: *Sema,
27896 block: *Block,
27897 src: LazySrcLoc,
27898 slice_src: LazySrcLoc,
27899 slice: Air.Inst.Ref,
27900 elem_index_src: LazySrcLoc,
27901 elem_index: Air.Inst.Ref,
27902 oob_safety: bool,
27903) CompileError!Air.Inst.Ref {
27904 const pt = sema.pt;
27905 const zcu = pt.zcu;
27906 const slice_ty = sema.typeOf(slice);
27907 assert(slice_ty.isSlice(zcu));
27908 const slice_sent = slice_ty.sentinel(zcu) != null;
27909 const elem_ty = slice_ty.childType(zcu);
27910 elem_ty.assertHasLayout(zcu);
27911
27912 const maybe_undef_slice_val = sema.resolveValue(slice);
27913 // The index must not be undefined since it can be out of bounds.
27914 const offset: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: {
27915 break :o index_val.toUnsignedInt(zcu);
27916 } else null;
27917
27918 const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt);
27919 assert(elem_ptr_ty.childType(zcu).toIntern() == elem_ty.toIntern());
27920
27921 if (maybe_undef_slice_val) |slice_val| {
27922 if (slice_val.isUndef(zcu)) {
27923 return pt.undefRef(elem_ptr_ty);
27924 }
27925 const slice_len = slice_val.sliceLen(zcu);
27926 const slice_len_s = slice_len + @intFromBool(slice_sent);
27927 if (slice_len_s == 0) {
27928 return sema.fail(block, slice_src, "cannot index into empty slice", .{});
27929 }
27930 if (offset) |index| {
27931 if (index >= slice_len_s) {
27932 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
27933 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
27934 }
27935 return .fromValue(try slice_val.ptrElem(index, pt));
27936 }
27937 }
27938
27939 try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_src);
27940 try sema.validateRuntimeValue(block, slice_src, slice);
27941 try sema.checkLogicalPtrOperation(block, src, slice_ty);
27942
27943 if (oob_safety and block.wantSafety()) {
27944 const len_inst = len: {
27945 if (maybe_undef_slice_val) |slice_val|
27946 if (!slice_val.isUndef(zcu))
27947 break :len try pt.intRef(.usize, slice_val.sliceLen(zcu));
27948 break :len try block.addTyOp(.slice_len, .usize, slice);
27949 };
27950 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
27951 try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op);
27952 }
27953 if (elem_ty.abiSize(zcu) == 0) {
27954 // zero-bit child type; just extract the pointer and bitcast it
27955 const slice_ptr = try block.addTyOp(.slice_ptr, slice_ty.slicePtrFieldType(zcu), slice);
27956 return block.addTyOp(.ptr_cast, elem_ptr_ty, slice_ptr);
27957 }
27958 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
27959}
27960
27961pub fn coerce(
27962 sema: *Sema,
27963 block: *Block,
27964 dest_ty_unresolved: Type,
27965 inst: Air.Inst.Ref,
27966 inst_src: LazySrcLoc,
27967) CompileError!Air.Inst.Ref {
27968 return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) {
27969 error.NotCoercible => unreachable,
27970 else => |e| return e,
27971 };
27972}
27973
27974const CoercionError = CompileError || error{
27975 /// When coerce is called recursively, this error should be returned instead of using `fail`
27976 /// to ensure correct types in compile errors.
27977 NotCoercible,
27978};
27979
27980const CoerceOpts = struct {
27981 /// Should coerceExtra emit error messages.
27982 report_err: bool = true,
27983 /// Ignored if `report_err == false`.
27984 is_ret: bool = false,
27985 /// Should coercion to comptime_int emit an error message.
27986 no_cast_to_comptime_int: bool = false,
27987
27988 param_src: struct {
27989 func_inst: Air.Inst.Ref = .none,
27990 param_i: u32 = undefined,
27991
27992 fn get(info: @This(), sema: *Sema) !?LazySrcLoc {
27993 if (info.func_inst == .none) return null;
27994 const func_inst = try sema.funcDeclSrcInst(info.func_inst) orelse return null;
27995 return .{
27996 .base_node_inst = func_inst,
27997 .offset = .{ .fn_proto_param_type = .{
27998 .fn_proto_node_offset = .zero,
27999 .param_index = info.param_i,
28000 } },
28001 };
28002 }
28003 } = .{ .func_inst = .none, .param_i = undefined },
28004};
28005
28006fn coerceExtra(
28007 sema: *Sema,
28008 block: *Block,
28009 dest_ty: Type,
28010 inst: Air.Inst.Ref,
28011 inst_src: LazySrcLoc,
28012 opts: CoerceOpts,
28013) CoercionError!Air.Inst.Ref {
28014 const pt = sema.pt;
28015 const zcu = pt.zcu;
28016 const comp = zcu.comp;
28017 const gpa = comp.gpa;
28018 const io = comp.io;
28019 const ip = &zcu.intern_pool;
28020
28021 if (dest_ty.isGenericPoison()) return inst;
28022
28023 const dest_ty_src = inst_src; // TODO better source location
28024 const inst_ty = sema.typeOf(inst);
28025 const target = zcu.getTarget();
28026
28027 inst_ty.assertHasLayout(zcu);
28028 try sema.ensureLayoutResolved(dest_ty, inst_src, .coerce);
28029
28030 // If the types are the same, we can return the operand.
28031 if (dest_ty.eql(inst_ty))
28032 return inst;
28033
28034 const maybe_inst_val = sema.resolveValue(inst);
28035
28036 var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
28037 if (in_memory_result == .ok) {
28038 if (maybe_inst_val) |val| {
28039 return .fromValue(try pt.getCoerced(val, dest_ty));
28040 }
28041 const coerced: Air.Inst.Ref = switch (in_memory_result.ok) {
28042 .none => coerced: {
28043 const @"addrspace" = target_util.defaultAddressSpace(zcu.getTarget(), .local);
28044 const src_ptr_ty = try pt.ptrType(.{
28045 .child = inst_ty.toIntern(),
28046 .flags = .{ .size = .one, .address_space = @"addrspace" },
28047 });
28048 const dest_ptr_ty = try pt.ptrType(.{
28049 .child = dest_ty.toIntern(),
28050 .flags = .{ .size = .one, .address_space = @"addrspace" },
28051 });
28052 const ptr = try block.addTy(.alloc, src_ptr_ty);
28053 _ = try block.addBinOp(.store_safe, ptr, inst);
28054 const casted_ptr = try block.addTyOp(.ptr_cast, dest_ptr_ty, ptr);
28055 break :coerced try block.addTyOp(.load, dest_ty, casted_ptr);
28056 },
28057 .same_type => unreachable, // we checked for equal types just above
28058 .bit_cast => try block.addTyOp(.bit_cast, dest_ty, inst),
28059 .ptr_cast => try block.addTyOp(.ptr_cast, dest_ty, inst),
28060 .error_cast => try block.addTyOp(.error_cast, dest_ty, inst),
28061 };
28062 try sema.checkKnownAllocPtr(block, inst, coerced);
28063 return coerced;
28064 }
28065
28066 switch (dest_ty.zigTypeTag(zcu)) {
28067 .optional => optional: {
28068 if (maybe_inst_val) |val| {
28069 // undefined sets the optional bit also to undefined.
28070 if (val.toIntern() == .undef) {
28071 return .fromValue(try dest_ty.onePossibleValue(pt) orelse try pt.undefValue(dest_ty));
28072 }
28073
28074 // null to ?T
28075 if (val.toIntern() == .null_value) {
28076 return Air.internedToRef((try pt.intern(.{ .opt = .{
28077 .ty = dest_ty.toIntern(),
28078 .val = .none,
28079 } })));
28080 }
28081 }
28082
28083 // cast from ?*T and ?[*]T to ?*anyopaque
28084 // but don't do it if the source type is a double pointer
28085 if (dest_ty.isPtrLikeOptional(zcu) and
28086 dest_ty.nullablePtrElem(zcu).toIntern() == .anyopaque_type and
28087 inst_ty.isPtrAtRuntime(zcu))
28088 anyopaque_check: {
28089 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional;
28090 const elem_ty = inst_ty.nullablePtrElem(zcu);
28091 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) {
28092 in_memory_result = .{ .double_ptr_to_anyopaque = .{
28093 .actual = inst_ty,
28094 .wanted = dest_ty,
28095 } };
28096 break :optional;
28097 }
28098 // Let the logic below handle wrapping the optional now that
28099 // it has been checked to correctly coerce.
28100 if (!inst_ty.isPtrLikeOptional(zcu)) break :anyopaque_check;
28101 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
28102 }
28103
28104 // T to ?T
28105 const child_type = dest_ty.optionalChild(zcu);
28106 const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
28107 error.NotCoercible => {
28108 if (in_memory_result == .no_match) {
28109 // Try to give more useful notes
28110 in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
28111 }
28112 break :optional;
28113 },
28114 else => |e| return e,
28115 };
28116 return try sema.wrapOptional(block, dest_ty, intermediate, inst_src);
28117 },
28118 .pointer => pointer: {
28119 const dest_info = dest_ty.ptrInfo(zcu);
28120
28121 // Function body to function pointer.
28122 if (inst_ty.zigTypeTag(zcu) == .@"fn") {
28123 const fn_val = sema.resolveValue(inst).?;
28124 const fn_nav = switch (zcu.intern_pool.indexToKey(fn_val.toIntern())) {
28125 .func => |f| f.owner_nav,
28126 .@"extern" => |e| e.owner_nav,
28127 else => unreachable,
28128 };
28129 const inst_as_ptr = try sema.analyzeNavRef(block, inst_src, fn_nav);
28130 return sema.coerce(block, dest_ty, inst_as_ptr, inst_src);
28131 }
28132
28133 // *T to *[1]T
28134 single_item: {
28135 if (dest_info.flags.size != .one) break :single_item;
28136 if (!inst_ty.isSinglePointer(zcu)) break :single_item;
28137 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
28138 const ptr_elem_ty = inst_ty.childType(zcu);
28139 const array_ty: Type = .fromInterned(dest_info.child);
28140 if (array_ty.zigTypeTag(zcu) != .array) break :single_item;
28141 const array_elem_ty = array_ty.childType(zcu);
28142 if (array_ty.arrayLen(zcu) != 1) break :single_item;
28143 const dest_is_mut = !dest_info.flags.is_const;
28144 switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) {
28145 .ok => {},
28146 else => break :single_item,
28147 }
28148 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
28149 }
28150
28151 // Coercions where the source is a single pointer to an array.
28152 src_array_ptr: {
28153 if (!inst_ty.isSinglePointer(zcu)) break :src_array_ptr;
28154 if (dest_info.flags.size == .one) break :src_array_ptr; // `*[n]T` -> `*T` isn't valid
28155 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
28156 const array_ty = inst_ty.childType(zcu);
28157 if (array_ty.zigTypeTag(zcu) != .array) break :src_array_ptr;
28158 const array_elem_type = array_ty.childType(zcu);
28159 const dest_is_mut = !dest_info.flags.is_const;
28160
28161 const dst_elem_type: Type = .fromInterned(dest_info.child);
28162 const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src, null);
28163 switch (elem_res) {
28164 .ok => {},
28165 else => {
28166 in_memory_result = .{ .ptr_child = .{
28167 .child = try elem_res.dupe(sema.arena),
28168 .actual = array_elem_type,
28169 .wanted = dst_elem_type,
28170 } };
28171 break :src_array_ptr;
28172 },
28173 }
28174
28175 if (dest_info.sentinel != .none) {
28176 if (array_ty.sentinel(zcu)) |inst_sent| {
28177 if (dest_info.sentinel !=
28178 (try pt.getCoerced(inst_sent, dst_elem_type)).toIntern())
28179 {
28180 in_memory_result = .{ .ptr_sentinel = .{
28181 .actual = inst_sent,
28182 .wanted = Value.fromInterned(dest_info.sentinel),
28183 .ty = dst_elem_type,
28184 } };
28185 break :src_array_ptr;
28186 }
28187 } else {
28188 in_memory_result = .{ .ptr_sentinel = .{
28189 .actual = Value.@"unreachable",
28190 .wanted = Value.fromInterned(dest_info.sentinel),
28191 .ty = dst_elem_type,
28192 } };
28193 break :src_array_ptr;
28194 }
28195 }
28196
28197 switch (dest_info.flags.size) {
28198 .slice => {
28199 // *[N]T to []T
28200 return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src);
28201 },
28202 .c => {
28203 // *[N]T to [*c]T
28204 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
28205 },
28206 .many => {
28207 // *[N]T to [*]T
28208 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
28209 },
28210 .one => unreachable, // early exit at top of block
28211 }
28212 }
28213
28214 // coercion from C pointer
28215 if (inst_ty.isCPtr(zcu)) src_c_ptr: {
28216 if (dest_info.flags.size == .slice) break :src_c_ptr;
28217 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr;
28218 // In this case we must add a safety check because the C pointer
28219 // could be null.
28220 const src_elem_ty = inst_ty.childType(zcu);
28221 const dest_is_mut = !dest_info.flags.is_const;
28222 const dst_elem_type: Type = .fromInterned(dest_info.child);
28223 switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) {
28224 .ok => {},
28225 else => break :src_c_ptr,
28226 }
28227 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
28228 }
28229
28230 // cast from *T and [*]T to *anyopaque
28231 // but don't do it if the source type is a double pointer
28232 if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(zcu) == .pointer) to_anyopaque: {
28233 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer;
28234 const elem_ty = inst_ty.childType(zcu);
28235 if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) {
28236 in_memory_result = .{ .double_ptr_to_anyopaque = .{
28237 .actual = inst_ty,
28238 .wanted = dest_ty,
28239 } };
28240 break :pointer;
28241 }
28242 if (dest_ty.isSlice(zcu)) break :to_anyopaque;
28243 if (inst_ty.isSlice(zcu)) {
28244 in_memory_result = .{ .slice_to_anyopaque = .{
28245 .actual = inst_ty,
28246 .wanted = dest_ty,
28247 } };
28248 break :pointer;
28249 }
28250 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
28251 }
28252
28253 switch (dest_info.flags.size) {
28254 // coercion to C pointer
28255 .c => switch (inst_ty.zigTypeTag(zcu)) {
28256 .null => return Air.internedToRef(try pt.intern(.{ .ptr = .{
28257 .ty = dest_ty.toIntern(),
28258 .base_addr = .int,
28259 .byte_offset = 0,
28260 } })),
28261 .comptime_int => {
28262 const addr = sema.coerceExtra(block, .usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
28263 error.NotCoercible => break :pointer,
28264 else => |e| return e,
28265 };
28266 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
28267 },
28268 .int => {
28269 const ptr_size_ty: Type = switch (inst_ty.intInfo(zcu).signedness) {
28270 .signed => .isize,
28271 .unsigned => .usize,
28272 };
28273 const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) {
28274 error.NotCoercible => {
28275 // Try to give more useful notes
28276 in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
28277 break :pointer;
28278 },
28279 else => |e| return e,
28280 };
28281 return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src);
28282 },
28283 .pointer => p: {
28284 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
28285 const inst_info = inst_ty.ptrInfo(zcu);
28286 switch (try sema.coerceInMemoryAllowed(
28287 block,
28288 .fromInterned(dest_info.child),
28289 .fromInterned(inst_info.child),
28290 !dest_info.flags.is_const,
28291 target,
28292 dest_ty_src,
28293 inst_src,
28294 null,
28295 )) {
28296 .ok => {},
28297 else => break :p,
28298 }
28299 if (inst_info.flags.size == .slice) {
28300 assert(dest_info.sentinel == .none);
28301 if (inst_info.sentinel == .none or
28302 inst_info.sentinel != (try pt.intValue(.fromInterned(inst_info.child), 0)).toIntern())
28303 break :p;
28304
28305 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
28306 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
28307 }
28308 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
28309 },
28310 else => {},
28311 },
28312 // []T to *[n]T
28313 .one => slice_to_array_ptr: {
28314 if (!inst_ty.isSlice(zcu)) break :slice_to_array_ptr;
28315 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :slice_to_array_ptr;
28316 const array_ty: Type = .fromInterned(dest_info.child);
28317 if (array_ty.zigTypeTag(zcu) != .array) break :slice_to_array_ptr;
28318 const inst_val = maybe_inst_val orelse {
28319 if (!opts.report_err) return error.NotCoercible;
28320 return sema.fail(
28321 block,
28322 inst_src,
28323 "coercion from slice to array pointer type '{f}' requires length to be known at compile-time",
28324 .{dest_ty.fmt(pt)},
28325 );
28326 };
28327
28328 const slice: InternPool.Key.Slice = slice: {
28329 switch (ip.indexToKey(inst_val.toIntern())) {
28330 .undef => {},
28331 .slice => |slice| if (slice.len != .undef_usize) break :slice slice,
28332 else => unreachable,
28333 }
28334 if (!opts.report_err) return error.NotCoercible;
28335 return sema.failWithOwnedErrorMsg(block, msg: {
28336 const msg = try sema.errMsg(inst_src, "slice with undefined length cannot cast into array pointer type '{f}'", .{
28337 dest_ty.fmt(pt),
28338 });
28339 errdefer msg.destroy(gpa);
28340 try sema.errNote(inst_src, msg, "length of slice must be defined and match length of array type", .{});
28341 break :msg msg;
28342 });
28343 };
28344 const slice_len = Value.fromInterned(slice.len).toUnsignedInt(zcu);
28345 if (array_ty.arrayLen(zcu) != slice_len) {
28346 if (!opts.report_err) return error.NotCoercible;
28347 return sema.failWithOwnedErrorMsg(block, msg: {
28348 const msg = try sema.errMsg(inst_src, "slice of length {d} cannot cast into array pointer type '{f}'", .{
28349 slice_len, dest_ty.fmt(pt),
28350 });
28351 errdefer msg.destroy(gpa);
28352 try sema.errNote(inst_src, msg, "length of slice must match length of array type", .{});
28353 break :msg msg;
28354 });
28355 }
28356
28357 const inst_elem_ty = inst_ty.childType(zcu);
28358 const dest_elem_ty = array_ty.childType(zcu);
28359 const dest_is_mut = !dest_info.flags.is_const;
28360 switch (try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) {
28361 .ok => {},
28362 else => |elem_res| {
28363 in_memory_result = .{ .ptr_child = .{
28364 .child = try elem_res.dupe(sema.arena),
28365 .actual = inst_elem_ty,
28366 .wanted = dest_elem_ty,
28367 } };
28368 break :slice_to_array_ptr;
28369 },
28370 }
28371
28372 if (array_ty.sentinel(zcu)) |array_sentinel| {
28373 if (inst_ty.sentinel(zcu)) |slice_sentinel| {
28374 if (array_sentinel.toIntern() !=
28375 (try pt.getCoerced(slice_sentinel, dest_elem_ty)).toIntern())
28376 {
28377 in_memory_result = .{ .ptr_sentinel = .{
28378 .actual = slice_sentinel,
28379 .wanted = array_sentinel,
28380 .ty = dest_elem_ty,
28381 } };
28382 break :slice_to_array_ptr;
28383 }
28384 } else {
28385 in_memory_result = .{ .ptr_sentinel = .{
28386 .actual = .@"unreachable",
28387 .wanted = array_sentinel,
28388 .ty = dest_elem_ty,
28389 } };
28390 break :slice_to_array_ptr;
28391 }
28392 }
28393
28394 const array_ptr = try pt.sliceToArrayPtr(slice);
28395 return sema.coerceCompatiblePtrs(block, dest_ty, .fromValue(array_ptr), inst_src);
28396 },
28397 .slice => to_slice: {
28398 if (inst_ty.zigTypeTag(zcu) == .array) {
28399 if (!opts.report_err) return error.NotCoercible;
28400 return sema.fail(
28401 block,
28402 inst_src,
28403 "array literal requires address-of operator (&) to coerce to slice type '{f}'",
28404 .{dest_ty.fmt(pt)},
28405 );
28406 }
28407
28408 if (!inst_ty.isSinglePointer(zcu)) break :to_slice;
28409 const inst_child_ty = inst_ty.childType(zcu);
28410 if (!inst_child_ty.isTuple(zcu)) break :to_slice;
28411
28412 // empty tuple to zero-length slice
28413 // note that this allows coercing to a mutable slice.
28414 if (inst_child_ty.structFieldCount(zcu) == 0) {
28415 const empty_array_ty = try pt.arrayType(.{
28416 .len = 0,
28417 .child = dest_info.child,
28418 .sentinel = dest_info.sentinel,
28419 });
28420 const empty_array_val = try pt.aggregateValue(empty_array_ty, &.{});
28421 const empty_array_ptr = try sema.uavRef(empty_array_val);
28422 return sema.coerceArrayPtrToSlice(block, dest_ty, empty_array_ptr, inst_src);
28423 }
28424
28425 // pointer to tuple to slice
28426 if (!dest_info.flags.is_const) {
28427 if (!opts.report_err) return error.NotCoercible;
28428 const err_msg = err_msg: {
28429 const err_msg = try sema.errMsg(inst_src, "cannot cast pointer to tuple to '{f}'", .{dest_ty.fmt(pt)});
28430 errdefer err_msg.destroy(sema.gpa);
28431 try sema.errNote(dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{});
28432 break :err_msg err_msg;
28433 };
28434 return sema.failWithOwnedErrorMsg(block, err_msg);
28435 }
28436 return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
28437 },
28438 .many => p: {
28439 if (!inst_ty.isSlice(zcu)) break :p;
28440 if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p;
28441 const inst_info = inst_ty.ptrInfo(zcu);
28442
28443 switch (try sema.coerceInMemoryAllowed(
28444 block,
28445 .fromInterned(dest_info.child),
28446 .fromInterned(inst_info.child),
28447 !dest_info.flags.is_const,
28448 target,
28449 dest_ty_src,
28450 inst_src,
28451 null,
28452 )) {
28453 .ok => {},
28454 else => break :p,
28455 }
28456
28457 if (dest_info.sentinel == .none or inst_info.sentinel == .none or
28458 dest_info.sentinel !=
28459 (try pt.getCoerced(.fromInterned(inst_info.sentinel), .fromInterned(dest_info.child))).toIntern())
28460 break :p;
28461
28462 const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty);
28463 return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src);
28464 },
28465 }
28466 },
28467 .int, .comptime_int => switch (inst_ty.zigTypeTag(zcu)) {
28468 .float, .comptime_float => float: {
28469 const val = maybe_inst_val orelse {
28470 if (dest_ty.zigTypeTag(zcu) == .comptime_int) {
28471 if (!opts.report_err) return error.NotCoercible;
28472 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int });
28473 }
28474 break :float;
28475 };
28476 const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact);
28477 return Air.internedToRef(result_val.toIntern());
28478 },
28479 .int, .comptime_int => {
28480 if (maybe_inst_val) |val| {
28481 // comptime-known integer to other number
28482 if (!val.intFitsInType(dest_ty, null, zcu)) {
28483 if (!opts.report_err) return error.NotCoercible;
28484 return sema.fail(block, inst_src, "type '{f}' cannot represent integer value '{f}'", .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) });
28485 }
28486 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
28487 .undef => .fromValue(try dest_ty.onePossibleValue(pt) orelse try pt.undefValue(dest_ty)),
28488 .int => |int| Air.internedToRef(
28489 try zcu.intern_pool.getCoercedInts(gpa, io, pt.tid, int, dest_ty.toIntern()),
28490 ),
28491 else => unreachable,
28492 };
28493 }
28494 if (dest_ty.zigTypeTag(zcu) == .comptime_int) {
28495 if (!opts.report_err) return error.NotCoercible;
28496 if (opts.no_cast_to_comptime_int) return inst;
28497 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int });
28498 }
28499
28500 // integer widening
28501 const dst_info = dest_ty.intInfo(zcu);
28502 const src_info = inst_ty.intInfo(zcu);
28503 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
28504 // small enough unsigned ints can get casted to large enough signed ints
28505 (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
28506 {
28507 try sema.requireRuntimeBlock(block, inst_src, null);
28508 return block.addTyOp(.int_cast, dest_ty, inst);
28509 }
28510 },
28511 else => {},
28512 },
28513 .float, .comptime_float => switch (inst_ty.zigTypeTag(zcu)) {
28514 .comptime_float => {
28515 const val = sema.resolveValue(inst).?;
28516 const result_val = try val.floatCast(dest_ty, pt);
28517 return Air.internedToRef(result_val.toIntern());
28518 },
28519 .float => {
28520 if (maybe_inst_val) |val| {
28521 const result_val = try val.floatCast(dest_ty, pt);
28522 if (!val.eql(try result_val.floatCast(inst_ty, pt), inst_ty, zcu)) {
28523 if (!opts.report_err) return error.NotCoercible;
28524 return sema.fail(
28525 block,
28526 inst_src,
28527 "type '{f}' cannot represent float value '{f}'",
28528 .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) },
28529 );
28530 }
28531 return Air.internedToRef(result_val.toIntern());
28532 } else if (dest_ty.zigTypeTag(zcu) == .comptime_float) {
28533 if (!opts.report_err) return error.NotCoercible;
28534 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float });
28535 }
28536
28537 // float widening
28538 const src_bits = inst_ty.floatBits(target);
28539 const dst_bits = dest_ty.floatBits(target);
28540 if (dst_bits >= src_bits) {
28541 try sema.requireRuntimeBlock(block, inst_src, null);
28542 return block.addTyOp(.fpext, dest_ty, inst);
28543 }
28544 },
28545 .int, .comptime_int => int: {
28546 const val = maybe_inst_val orelse {
28547 if (dest_ty.zigTypeTag(zcu) == .comptime_float) {
28548 if (!opts.report_err) return error.NotCoercible;
28549 return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float });
28550 }
28551 const int_info = inst_ty.intInfo(zcu);
28552 const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed);
28553 if (int_precision <= dest_ty.floatSignificandBits(target)) {
28554 try sema.requireRuntimeBlock(block, inst_src, null);
28555 return block.addTyOp(.float_from_int, dest_ty, inst);
28556 }
28557 break :int;
28558 };
28559 if (val.isUndef(zcu)) {
28560 return .fromValue(try pt.undefValue(dest_ty));
28561 }
28562 const result_val = try pt.floatValue(dest_ty, val.toFloat(f128, zcu));
28563 const float = ip.indexToKey(result_val.toIntern()).float;
28564 var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined;
28565 const operand_big_int = val.toBigInt(&buffer, zcu);
28566 const fits = switch (float.storage) {
28567 inline else => |x| fits: {
28568 if (!std.math.isFinite(x)) break :fits false;
28569 var result_big_int: std.math.big.int.Mutable = .{
28570 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)),
28571 .len = undefined,
28572 .positive = undefined,
28573 };
28574 switch (result_big_int.setFloat(x, .nearest_even)) {
28575 .inexact => break :fits false,
28576 .exact => {},
28577 }
28578 break :fits result_big_int.toConst().eql(operand_big_int);
28579 },
28580 };
28581 if (!fits) {
28582 if (!opts.report_err) return error.NotCoercible;
28583 return sema.fail(
28584 block,
28585 inst_src,
28586 "type '{f}' cannot represent integer value '{f}'",
28587 .{ dest_ty.fmt(pt), val.fmtValue(pt) },
28588 );
28589 }
28590 return .fromValue(result_val);
28591 },
28592 else => {},
28593 },
28594 .@"enum" => switch (inst_ty.zigTypeTag(zcu)) {
28595 .enum_literal => {
28596 // enum literal to enum
28597 const val = sema.resolveValue(inst).?;
28598 const string = zcu.intern_pool.indexToKey(val.toIntern()).enum_literal;
28599 const field_index = dest_ty.enumFieldIndex(string, zcu) orelse {
28600 if (!opts.report_err) return error.NotCoercible;
28601 return sema.fail(block, inst_src, "no field named '{f}' in enum '{f}'", .{
28602 string.fmt(&zcu.intern_pool), dest_ty.fmt(pt),
28603 });
28604 };
28605 return Air.internedToRef((try pt.enumValueFieldIndex(dest_ty, @intCast(field_index))).toIntern());
28606 },
28607 .@"union" => if (inst_ty.unionTagType(zcu)) |enum_tag_ty| {
28608 // union to its own tag type
28609 if (enum_tag_ty.toIntern() == dest_ty.toIntern()) {
28610 return sema.unionToTag(block, inst);
28611 }
28612 },
28613 else => {},
28614 },
28615 .error_union => switch (inst_ty.zigTypeTag(zcu)) {
28616 // E to E!T
28617 .error_set => if (sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src)) |res| {
28618 return res;
28619 } else |err| switch (err) {
28620 error.NotCoercible => if (in_memory_result == .no_match) {
28621 // Try to give more useful notes
28622 const err_set_type = dest_ty.errorUnionSet(zcu);
28623 in_memory_result = try sema.coerceInMemoryAllowed(block, err_set_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
28624 },
28625 else => |e| return e,
28626 },
28627 // T to E!T
28628 else => if (sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src)) |res| {
28629 return res;
28630 } else |err| switch (err) {
28631 error.NotCoercible => if (in_memory_result == .no_match) {
28632 // Try to give more useful notes
28633 const payload_type = dest_ty.errorUnionPayload(zcu);
28634 in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val);
28635 },
28636 else => |e| return e,
28637 },
28638 },
28639 .@"union" => switch (inst_ty.zigTypeTag(zcu)) {
28640 .@"enum", .enum_literal => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src),
28641 else => {},
28642 },
28643 .array => switch (inst_ty.zigTypeTag(zcu)) {
28644 .array => array_to_array: {
28645 // Array coercions are allowed only if the child is IMC and the sentinel is unchanged or removed.
28646 if (.ok != try sema.coerceInMemoryAllowed(
28647 block,
28648 dest_ty.childType(zcu),
28649 inst_ty.childType(zcu),
28650 false,
28651 target,
28652 dest_ty_src,
28653 inst_src,
28654 null,
28655 )) {
28656 break :array_to_array;
28657 }
28658
28659 if (dest_ty.sentinel(zcu)) |dest_sent| {
28660 const src_sent = inst_ty.sentinel(zcu) orelse break :array_to_array;
28661 if (dest_sent.toIntern() != (try pt.getCoerced(src_sent, dest_ty.childType(zcu))).toIntern()) {
28662 break :array_to_array;
28663 }
28664 }
28665
28666 return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src);
28667 },
28668 .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
28669 .@"struct" => {
28670 if (inst_ty.isTuple(zcu)) {
28671 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
28672 }
28673 },
28674 else => {},
28675 },
28676 .vector => switch (inst_ty.zigTypeTag(zcu)) {
28677 .array, .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src),
28678 .@"struct" => {
28679 if (inst_ty.isTuple(zcu)) {
28680 return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src);
28681 }
28682 },
28683 else => {},
28684 },
28685 .@"struct" => blk: {
28686 if (dest_ty.isTuple(zcu) and inst_ty.isTuple(zcu)) {
28687 return sema.coerceTupleToTuple(block, dest_ty, inst, inst_src) catch |err| switch (err) {
28688 error.NotCoercible => break :blk,
28689 else => |e| return e,
28690 };
28691 }
28692 },
28693 else => {},
28694 }
28695
28696 const dest_is_npv = switch (dest_ty.classify(zcu)) {
28697 .no_possible_value => true,
28698 .one_possible_value => if (inst == .undef) {
28699 return .fromValue((try dest_ty.onePossibleValue(pt)).?);
28700 } else false,
28701 .runtime, .fully_comptime, .partially_comptime => if (inst == .undef) {
28702 return .fromValue(try pt.undefValue(dest_ty));
28703 } else false,
28704 };
28705
28706 if (!opts.report_err) return error.NotCoercible;
28707
28708 if (opts.is_ret and dest_ty.zigTypeTag(zcu) == .noreturn) {
28709 const msg = msg: {
28710 const msg = try sema.errMsg(inst_src, "function declared 'noreturn' returns", .{});
28711 errdefer msg.destroy(sema.gpa);
28712
28713 const ret_ty_src: LazySrcLoc = .{
28714 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip),
28715 .offset = .{ .node_offset_fn_type_ret_ty = .zero },
28716 };
28717 try sema.errNote(ret_ty_src, msg, "'noreturn' declared here", .{});
28718 break :msg msg;
28719 };
28720 return sema.failWithOwnedErrorMsg(block, msg);
28721 }
28722
28723 const msg = msg: {
28724 const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty);
28725 errdefer msg.destroy(sema.gpa);
28726
28727 if (dest_is_npv) {
28728 try sema.errNote(inst_src, msg, "cannot coerce to uninstantiable type '{f}'", .{dest_ty.fmt(pt)});
28729 }
28730
28731 // E!T to T
28732 if (inst_ty.zigTypeTag(zcu) == .error_union and
28733 (try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty.errorUnionPayload(zcu), false, target, dest_ty_src, inst_src, null)) == .ok)
28734 {
28735 try sema.errNote(inst_src, msg, "cannot convert error union to payload type", .{});
28736 try sema.errNote(inst_src, msg, "consider using 'try', 'catch', or 'if'", .{});
28737 }
28738
28739 // ?T to T
28740 if (inst_ty.zigTypeTag(zcu) == .optional and
28741 (try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty.optionalChild(zcu), false, target, dest_ty_src, inst_src, null)) == .ok)
28742 {
28743 try sema.errNote(inst_src, msg, "cannot convert optional to payload type", .{});
28744 try sema.errNote(inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
28745 }
28746
28747 try in_memory_result.report(sema, inst_src, msg);
28748
28749 // Add notes about function return type
28750 if (opts.is_ret and
28751 !zcu.test_functions.contains(zcu.funcInfo(sema.func_index).owner_nav))
28752 {
28753 const ret_ty_src: LazySrcLoc = .{
28754 .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip),
28755 .offset = .{ .node_offset_fn_type_ret_ty = .zero },
28756 };
28757 if (inst_ty.isError(zcu) and !dest_ty.isError(zcu)) {
28758 try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{});
28759 } else {
28760 try sema.errNote(ret_ty_src, msg, "function return type declared here", .{});
28761 }
28762 }
28763
28764 if (try opts.param_src.get(sema)) |param_src| {
28765 try sema.errNote(param_src, msg, "parameter type declared here", .{});
28766 }
28767
28768 // TODO maybe add "cannot store an error in type '{f}'" note
28769
28770 break :msg msg;
28771 };
28772 return sema.failWithOwnedErrorMsg(block, msg);
28773}
28774
28775const InMemoryCoercionResult = union(enum) {
28776 ok: Strategy,
28777 no_match: Pair,
28778 int_not_coercible: Int,
28779 comptime_int_not_coercible: TypeValuePair,
28780 error_union_payload: PairAndChild,
28781 array_len: IntPair,
28782 array_sentinel: Sentinel,
28783 array_elem: PairAndChild,
28784 vector_len: IntPair,
28785 vector_elem: PairAndChild,
28786 optional_shape: Pair,
28787 optional_child: PairAndChild,
28788 from_anyerror,
28789 missing_error: []const InternPool.NullTerminatedString,
28790 /// true if wanted is var args
28791 fn_var_args: bool,
28792 /// true if wanted is generic
28793 fn_generic: bool,
28794 fn_param_count: IntPair,
28795 fn_param_noalias: IntPair,
28796 fn_param_comptime: ComptimeParam,
28797 fn_param: Param,
28798 fn_cc: CC,
28799 fn_return_type: PairAndChild,
28800 ptr_child: PairAndChild,
28801 ptr_addrspace: AddressSpace,
28802 ptr_sentinel: Sentinel,
28803 ptr_size: Size,
28804 ptr_const: Pair,
28805 ptr_volatile: Pair,
28806 ptr_allowzero: Pair,
28807 ptr_bit_range: BitRange,
28808 ptr_alignment: AlignPair,
28809 double_ptr_to_anyopaque: Pair,
28810 slice_to_anyopaque: Pair,
28811
28812 const Strategy = enum {
28813 /// There isn't a special strategy for this particular coercion---we'll just need to
28814 /// reinterpret the bytes in memory.
28815 none,
28816
28817 /// The source and destination types are equal, so no explicit cast operation is necessary.
28818 same_type,
28819 /// The coercion can be lowered to `Air.Inst.Tag.bit_cast`.
28820 bit_cast,
28821 /// The coercion can be lowered to `Air.Inst.Tag.ptr_cast`.
28822 ptr_cast,
28823 /// The coercion can be lowered to `Air.Inst.Tag.error_cast`.
28824 error_cast,
28825 };
28826
28827 const Pair = struct {
28828 actual: Type,
28829 wanted: Type,
28830 };
28831
28832 const TypeValuePair = struct {
28833 actual: Value,
28834 wanted: Type,
28835 };
28836
28837 const PairAndChild = struct {
28838 child: *InMemoryCoercionResult,
28839 actual: Type,
28840 wanted: Type,
28841 };
28842
28843 const Param = struct {
28844 child: *InMemoryCoercionResult,
28845 actual: Type,
28846 wanted: Type,
28847 index: u64,
28848 };
28849
28850 const ComptimeParam = struct {
28851 index: u64,
28852 wanted: bool,
28853 };
28854
28855 const Sentinel = struct {
28856 // unreachable_value indicates no sentinel
28857 actual: Value,
28858 wanted: Value,
28859 ty: Type,
28860 };
28861
28862 const Int = struct {
28863 actual_signedness: std.lang.Signedness,
28864 wanted_signedness: std.lang.Signedness,
28865 actual_bits: u16,
28866 wanted_bits: u16,
28867 };
28868
28869 const IntPair = struct {
28870 actual: u64,
28871 wanted: u64,
28872 };
28873
28874 const AlignPair = struct {
28875 actual: Alignment,
28876 wanted: Alignment,
28877 };
28878
28879 const Size = struct {
28880 actual: std.lang.Type.Pointer.Size,
28881 wanted: std.lang.Type.Pointer.Size,
28882 };
28883
28884 const AddressSpace = struct {
28885 actual: std.lang.AddressSpace,
28886 wanted: std.lang.AddressSpace,
28887 };
28888
28889 const CC = struct {
28890 actual: std.lang.CallingConvention,
28891 wanted: std.lang.CallingConvention,
28892 };
28893
28894 const BitRange = struct {
28895 actual_host: u16,
28896 wanted_host: u16,
28897 actual_offset: u16,
28898 wanted_offset: u16,
28899 };
28900
28901 fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult {
28902 const res = try arena.create(InMemoryCoercionResult);
28903 res.* = child.*;
28904 return res;
28905 }
28906
28907 fn report(res: *const InMemoryCoercionResult, sema: *Sema, src: LazySrcLoc, msg: *Zcu.ErrorMsg) !void {
28908 const pt = sema.pt;
28909 var cur = res;
28910 while (true) switch (cur.*) {
28911 .ok => unreachable,
28912 .no_match => |types| {
28913 try sema.addDeclaredHereNote(msg, types.wanted);
28914 try sema.addDeclaredHereNote(msg, types.actual);
28915 break;
28916 },
28917 .int_not_coercible => |int| {
28918 try sema.errNote(src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{
28919 @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits,
28920 });
28921 break;
28922 },
28923 .comptime_int_not_coercible => |int| {
28924 try sema.errNote(src, msg, "type '{f}' cannot represent value '{f}'", .{
28925 int.wanted.fmt(pt), int.actual.fmtValueSema(pt, sema),
28926 });
28927 break;
28928 },
28929 .error_union_payload => |pair| {
28930 try sema.errNote(src, msg, "error union payload '{f}' cannot cast into error union payload '{f}'", .{
28931 pair.actual.fmt(pt), pair.wanted.fmt(pt),
28932 });
28933 cur = pair.child;
28934 },
28935 .array_len => |lens| {
28936 try sema.errNote(src, msg, "array of length {d} cannot cast into an array of length {d}", .{
28937 lens.actual, lens.wanted,
28938 });
28939 break;
28940 },
28941 .array_sentinel => |sentinel| {
28942 if (sentinel.wanted.toIntern() == .unreachable_value) {
28943 try sema.errNote(src, msg, "source array cannot be guaranteed to maintain '{f}' sentinel", .{
28944 sentinel.actual.fmtValueSema(pt, sema),
28945 });
28946 } else if (sentinel.actual.toIntern() == .unreachable_value) {
28947 try sema.errNote(src, msg, "destination array requires '{f}' sentinel", .{
28948 sentinel.wanted.fmtValueSema(pt, sema),
28949 });
28950 } else {
28951 try sema.errNote(src, msg, "array sentinel '{f}' cannot cast into array sentinel '{f}'", .{
28952 sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema),
28953 });
28954 }
28955 break;
28956 },
28957 .array_elem => |pair| {
28958 try sema.errNote(src, msg, "array element type '{f}' cannot cast into array element type '{f}'", .{
28959 pair.actual.fmt(pt), pair.wanted.fmt(pt),
28960 });
28961 cur = pair.child;
28962 },
28963 .vector_len => |lens| {
28964 try sema.errNote(src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{
28965 lens.actual, lens.wanted,
28966 });
28967 break;
28968 },
28969 .vector_elem => |pair| {
28970 try sema.errNote(src, msg, "vector element type '{f}' cannot cast into vector element type '{f}'", .{
28971 pair.actual.fmt(pt), pair.wanted.fmt(pt),
28972 });
28973 cur = pair.child;
28974 },
28975 .optional_shape => |pair| {
28976 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{
28977 pair.actual.optionalChild(pt.zcu).fmt(pt), pair.wanted.optionalChild(pt.zcu).fmt(pt),
28978 });
28979 break;
28980 },
28981 .optional_child => |pair| {
28982 try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{
28983 pair.actual.fmt(pt), pair.wanted.fmt(pt),
28984 });
28985 cur = pair.child;
28986 },
28987 .from_anyerror => {
28988 try sema.errNote(src, msg, "global error set cannot cast into a smaller set", .{});
28989 break;
28990 },
28991 .missing_error => |missing_errors| {
28992 for (missing_errors) |err| {
28993 try sema.errNote(src, msg, "'error.{f}' not a member of destination error set", .{err.fmt(&pt.zcu.intern_pool)});
28994 }
28995 break;
28996 },
28997 .fn_var_args => |wanted_var_args| {
28998 if (wanted_var_args) {
28999 try sema.errNote(src, msg, "non-variadic function cannot cast into a variadic function", .{});
29000 } else {
29001 try sema.errNote(src, msg, "variadic function cannot cast into a non-variadic function", .{});
29002 }
29003 break;
29004 },
29005 .fn_generic => |wanted_generic| {
29006 if (wanted_generic) {
29007 try sema.errNote(src, msg, "non-generic function cannot cast into a generic function", .{});
29008 } else {
29009 try sema.errNote(src, msg, "generic function cannot cast into a non-generic function", .{});
29010 }
29011 break;
29012 },
29013 .fn_param_count => |lens| {
29014 try sema.errNote(src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{
29015 lens.actual, lens.wanted,
29016 });
29017 break;
29018 },
29019 .fn_param_noalias => |param| {
29020 var index: u6 = 0;
29021 var actual_noalias = false;
29022 while (true) : (index += 1) {
29023 const actual: u1 = @truncate(param.actual >> index);
29024 const wanted: u1 = @truncate(param.wanted >> index);
29025 if (actual != wanted) {
29026 actual_noalias = actual == 1;
29027 break;
29028 }
29029 }
29030 if (!actual_noalias) {
29031 try sema.errNote(src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index});
29032 } else {
29033 try sema.errNote(src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index});
29034 }
29035 break;
29036 },
29037 .fn_param_comptime => |param| {
29038 if (param.wanted) {
29039 try sema.errNote(src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index});
29040 } else {
29041 try sema.errNote(src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index});
29042 }
29043 break;
29044 },
29045 .fn_param => |param| {
29046 try sema.errNote(src, msg, "parameter {d} '{f}' cannot cast into '{f}'", .{
29047 param.index, param.actual.fmt(pt), param.wanted.fmt(pt),
29048 });
29049 cur = param.child;
29050 },
29051 .fn_cc => |cc| {
29052 try sema.errNote(src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) });
29053 break;
29054 },
29055 .fn_return_type => |pair| {
29056 try sema.errNote(src, msg, "return type '{f}' cannot cast into return type '{f}'", .{
29057 pair.actual.fmt(pt), pair.wanted.fmt(pt),
29058 });
29059 cur = pair.child;
29060 },
29061 .ptr_child => |pair| {
29062 try sema.errNote(src, msg, "pointer type child '{f}' cannot cast into pointer type child '{f}'", .{
29063 pair.actual.fmt(pt), pair.wanted.fmt(pt),
29064 });
29065 cur = pair.child;
29066 },
29067 .ptr_addrspace => |@"addrspace"| {
29068 try sema.errNote(src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) });
29069 break;
29070 },
29071 .ptr_sentinel => |sentinel| {
29072 if (sentinel.actual.toIntern() != .unreachable_value) {
29073 try sema.errNote(src, msg, "pointer sentinel '{f}' cannot cast into pointer sentinel '{f}'", .{
29074 sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema),
29075 });
29076 } else {
29077 try sema.errNote(src, msg, "destination pointer requires '{f}' sentinel", .{
29078 sentinel.wanted.fmtValueSema(pt, sema),
29079 });
29080 }
29081 break;
29082 },
29083 .ptr_size => |size| {
29084 try sema.errNote(src, msg, "a {s} cannot cast into a {s}", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) });
29085 break;
29086 },
29087 .ptr_allowzero => |pair| {
29088 const wanted_allow_zero = pair.wanted.ptrAllowsZero(pt.zcu);
29089 const actual_allow_zero = pair.actual.ptrAllowsZero(pt.zcu);
29090 if (actual_allow_zero and !wanted_allow_zero) {
29091 try sema.errNote(src, msg, "'{f}' could have null values which are illegal in type '{f}'", .{
29092 pair.actual.fmt(pt), pair.wanted.fmt(pt),
29093 });
29094 } else {
29095 try sema.errNote(src, msg, "mutable '{f}' would allow illegal null values stored to type '{f}'", .{
29096 pair.wanted.fmt(pt), pair.actual.fmt(pt),
29097 });
29098 }
29099 break;
29100 },
29101 .ptr_const => |pair| {
29102 const wanted_const = pair.wanted.isConstPtr(pt.zcu);
29103 const actual_const = pair.actual.isConstPtr(pt.zcu);
29104 if (actual_const and !wanted_const) {
29105 try sema.errNote(src, msg, "cast discards const qualifier", .{});
29106 } else {
29107 try sema.errNote(src, msg, "mutable '{f}' would allow illegal const pointers stored to type '{f}'", .{
29108 pair.wanted.fmt(pt), pair.actual.fmt(pt),
29109 });
29110 }
29111 break;
29112 },
29113 .ptr_volatile => |pair| {
29114 const wanted_volatile = pair.wanted.isVolatilePtr(pt.zcu);
29115 const actual_volatile = pair.actual.isVolatilePtr(pt.zcu);
29116 if (actual_volatile and !wanted_volatile) {
29117 try sema.errNote(src, msg, "cast discards volatile qualifier", .{});
29118 } else {
29119 try sema.errNote(src, msg, "mutable '{f}' would allow illegal volatile pointers stored to type '{f}'", .{
29120 pair.wanted.fmt(pt), pair.actual.fmt(pt),
29121 });
29122 }
29123 break;
29124 },
29125 .ptr_bit_range => |bit_range| {
29126 if (bit_range.actual_host != bit_range.wanted_host) {
29127 try sema.errNote(src, msg, "pointer host size '{d}' cannot cast into pointer host size '{d}'", .{
29128 bit_range.actual_host, bit_range.wanted_host,
29129 });
29130 }
29131 if (bit_range.actual_offset != bit_range.wanted_offset) {
29132 try sema.errNote(src, msg, "pointer bit offset '{d}' cannot cast into pointer bit offset '{d}'", .{
29133 bit_range.actual_offset, bit_range.wanted_offset,
29134 });
29135 }
29136 break;
29137 },
29138 .ptr_alignment => |pair| {
29139 try sema.errNote(src, msg, "pointer alignment '{d}' cannot cast into pointer alignment '{d}'", .{
29140 pair.actual.toByteUnits() orelse 0, pair.wanted.toByteUnits() orelse 0,
29141 });
29142 break;
29143 },
29144 .double_ptr_to_anyopaque => |pair| {
29145 try sema.errNote(src, msg, "cannot implicitly cast double pointer '{f}' to anyopaque pointer '{f}'", .{
29146 pair.actual.fmt(pt), pair.wanted.fmt(pt),
29147 });
29148 break;
29149 },
29150 .slice_to_anyopaque => |pair| {
29151 try sema.errNote(src, msg, "cannot implicitly cast slice '{f}' to anyopaque pointer '{f}'", .{
29152 pair.actual.fmt(pt), pair.wanted.fmt(pt),
29153 });
29154 try sema.errNote(src, msg, "consider using '.ptr'", .{});
29155 break;
29156 },
29157 };
29158 }
29159};
29160
29161fn pointerSizeString(size: std.lang.Type.Pointer.Size) []const u8 {
29162 return switch (size) {
29163 .one => "single pointer",
29164 .many => "many pointer",
29165 .c => "C pointer",
29166 .slice => "slice",
29167 };
29168}
29169
29170/// If types `A` and `B` have identical representations in runtime memory, they are considered
29171/// "in-memory coercible". This is a subset of normal coercions. Not only can `A` coerce to `B`, but
29172/// also, coercions can happen through pointers. For instance, `*const A` can coerce to `*const B`.
29173///
29174/// If this function is called, the coercion must be applied, or a compile error emitted if `.ok`
29175/// is not returned. This is because this function may modify inferred error sets to make a
29176/// coercion possible, even if `.ok` is not returned.
29177pub fn coerceInMemoryAllowed(
29178 sema: *Sema,
29179 block: *Block,
29180 dest_ty: Type,
29181 src_ty: Type,
29182 /// If `true`, this query comes from an attempted coercion of the form `*Src` -> `*Dest`, where
29183 /// both pointers are mutable. If this coercion is allowed, one could store to the `*Dest` and
29184 /// load from the `*Src` to effectively perform an in-memory coercion from `Dest` to `Src`.
29185 /// Therefore, when `dest_is_mut`, the in-memory coercion must be valid in *both directions*.
29186 dest_is_mut: bool,
29187 target: *const std.Target,
29188 dest_src: LazySrcLoc,
29189 src_src: LazySrcLoc,
29190 src_val: ?Value,
29191) CompileError!InMemoryCoercionResult {
29192 const pt = sema.pt;
29193 const zcu = pt.zcu;
29194
29195 if (src_val) |val| {
29196 assert(val.typeOf(zcu).toIntern() == src_ty.toIntern());
29197 }
29198
29199 if (dest_ty.eql(src_ty))
29200 return .{ .ok = .same_type };
29201
29202 const dest_tag = dest_ty.zigTypeTag(zcu);
29203 const src_tag = src_ty.zigTypeTag(zcu);
29204
29205 // Differently-named integers with the same number of bits.
29206 if (dest_tag == .int and src_tag == .int) {
29207 const dest_info = dest_ty.intInfo(zcu);
29208 const src_info = src_ty.intInfo(zcu);
29209
29210 if (dest_info.signedness == src_info.signedness and
29211 dest_info.bits == src_info.bits)
29212 {
29213 return .{ .ok = .bit_cast };
29214 }
29215
29216 if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or
29217 // small enough unsigned ints can get casted to large enough signed ints
29218 (dest_info.signedness == .signed and src_info.signedness == .unsigned and dest_info.bits <= src_info.bits) or
29219 (dest_info.signedness == .unsigned and src_info.signedness == .signed))
29220 {
29221 return .{ .int_not_coercible = .{
29222 .actual_signedness = src_info.signedness,
29223 .wanted_signedness = dest_info.signedness,
29224 .actual_bits = src_info.bits,
29225 .wanted_bits = dest_info.bits,
29226 } };
29227 }
29228 }
29229
29230 // Comptime int to regular int.
29231 if (dest_tag == .int and src_tag == .comptime_int) {
29232 if (src_val) |val| {
29233 if (!val.intFitsInType(dest_ty, null, zcu)) {
29234 return .{ .comptime_int_not_coercible = .{ .wanted = dest_ty, .actual = val } };
29235 }
29236 }
29237 }
29238
29239 // Differently-named floats with the same number of bits.
29240 if (dest_tag == .float and src_tag == .float) {
29241 const dest_bits = dest_ty.floatBits(target);
29242 const src_bits = src_ty.floatBits(target);
29243 if (dest_bits == src_bits) {
29244 return .{ .ok = .bit_cast };
29245 }
29246 }
29247
29248 // Pointers / Pointer-like Optionals
29249 if (dest_ty.isPtrAtRuntime(zcu) and src_ty.isPtrAtRuntime(zcu)) {
29250 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
29251 }
29252
29253 // Slices
29254 if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) {
29255 return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
29256 }
29257
29258 // Functions
29259 if (dest_tag == .@"fn" and src_tag == .@"fn") {
29260 return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src);
29261 }
29262
29263 // Error Unions
29264 if (dest_tag == .error_union and src_tag == .error_union) {
29265 const dest_payload = dest_ty.errorUnionPayload(zcu);
29266 const src_payload = src_ty.errorUnionPayload(zcu);
29267 const payload_strat = switch (try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src, null)) {
29268 .ok => |strat| strat,
29269 else => |payload_result| return .{ .error_union_payload = .{
29270 .child = try payload_result.dupe(sema.arena),
29271 .actual = src_payload,
29272 .wanted = dest_payload,
29273 } },
29274 };
29275 switch (try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(zcu), src_ty.errorUnionSet(zcu), dest_is_mut, target, dest_src, src_src, null)) {
29276 .ok => {},
29277 else => |err_set_result| return err_set_result,
29278 }
29279 return switch (payload_strat) {
29280 .same_type => .{ .ok = .error_cast },
29281 else => .{ .ok = .none },
29282 };
29283 }
29284
29285 // Error Sets
29286 if (dest_tag == .error_set and src_tag == .error_set) {
29287 switch (try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src)) {
29288 .ok => |strat| assert(strat == .error_cast),
29289 else => |result| return result,
29290 }
29291 if (dest_is_mut) {
29292 // src -> dest is okay, but `dest_is_mut`, so it needs to be allowed in the other direction.
29293 switch (try sema.coerceInMemoryAllowedErrorSets(block, src_ty, dest_ty, src_src, dest_src)) {
29294 .ok => |strat| assert(strat == .error_cast),
29295 else => |result| return result,
29296 }
29297 }
29298 return .{ .ok = .error_cast };
29299 }
29300
29301 // Arrays
29302 if (dest_tag == .array and src_tag == .array) {
29303 const dest_info = dest_ty.arrayInfo(zcu);
29304 const src_info = src_ty.arrayInfo(zcu);
29305 if (dest_info.len != src_info.len) {
29306 return .{ .array_len = .{
29307 .actual = src_info.len,
29308 .wanted = dest_info.len,
29309 } };
29310 }
29311
29312 const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src, null);
29313 const child_strat = switch (child) {
29314 .ok => |strat| strat,
29315 .no_match => |no_match| return .{ .no_match = no_match },
29316 else => {
29317 return .{ .array_elem = .{
29318 .child = try child.dupe(sema.arena),
29319 .actual = src_info.elem_type,
29320 .wanted = dest_info.elem_type,
29321 } };
29322 },
29323 };
29324 const ok_sent = (dest_info.sentinel == null and src_info.sentinel == null) or
29325 (src_info.sentinel != null and
29326 dest_info.sentinel != null and
29327 dest_info.sentinel.?.eql(
29328 try pt.getCoerced(src_info.sentinel.?, dest_info.elem_type),
29329 dest_info.elem_type,
29330 zcu,
29331 ));
29332 if (!ok_sent) {
29333 return .{ .array_sentinel = .{
29334 .actual = src_info.sentinel orelse Value.@"unreachable",
29335 .wanted = dest_info.sentinel orelse Value.@"unreachable",
29336 .ty = dest_info.elem_type,
29337 } };
29338 }
29339 return .{ .ok = switch (child_strat) {
29340 .bit_cast => .bit_cast,
29341 else => .none,
29342 } };
29343 }
29344
29345 // Vectors
29346 if (dest_tag == .vector and src_tag == .vector) {
29347 const dest_len = dest_ty.vectorLen(zcu);
29348 const src_len = src_ty.vectorLen(zcu);
29349 if (dest_len != src_len) {
29350 return .{ .vector_len = .{
29351 .actual = src_len,
29352 .wanted = dest_len,
29353 } };
29354 }
29355
29356 const dest_elem_ty = dest_ty.scalarType(zcu);
29357 const src_elem_ty = src_ty.scalarType(zcu);
29358 switch (try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null)) {
29359 .ok => |child_strat| return .{ .ok = switch (child_strat) {
29360 .bit_cast => .bit_cast,
29361 .ptr_cast => .ptr_cast,
29362 else => .none,
29363 } },
29364 else => |child_result| return .{ .vector_elem = .{
29365 .child = try child_result.dupe(sema.arena),
29366 .actual = src_elem_ty,
29367 .wanted = dest_elem_ty,
29368 } },
29369 }
29370 }
29371
29372 // Optionals
29373 if (dest_tag == .optional and src_tag == .optional) {
29374 if (dest_ty.isPtrAtRuntime(zcu) or src_ty.isPtrAtRuntime(zcu)) {
29375 // Only one is, because we already handled when both are.
29376 return .{ .optional_shape = .{
29377 .actual = src_ty,
29378 .wanted = dest_ty,
29379 } };
29380 }
29381 const dest_child_type = dest_ty.optionalChild(zcu);
29382 const src_child_type = src_ty.optionalChild(zcu);
29383
29384 const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src, null);
29385 if (child != .ok) {
29386 return .{ .optional_child = .{
29387 .child = try child.dupe(sema.arena),
29388 .actual = src_child_type,
29389 .wanted = dest_child_type,
29390 } };
29391 }
29392
29393 return .{ .ok = .none };
29394 }
29395
29396 // Tuples (with in-memory-coercible fields)
29397 if (dest_ty.isTuple(zcu) and src_ty.isTuple(zcu)) tuple: {
29398 if (dest_ty.structFieldCount(zcu) != src_ty.structFieldCount(zcu)) break :tuple;
29399 const field_count = dest_ty.structFieldCount(zcu);
29400 for (0..field_count) |field_idx| {
29401 if (dest_ty.structFieldIsComptime(field_idx, zcu) != src_ty.structFieldIsComptime(field_idx, zcu)) break :tuple;
29402 const dest_field_ty = dest_ty.fieldType(field_idx, zcu);
29403 const src_field_ty = src_ty.fieldType(field_idx, zcu);
29404 const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src, null);
29405 if (field != .ok) break :tuple;
29406 }
29407 return .{ .ok = .none };
29408 }
29409
29410 return .{ .no_match = .{
29411 .actual = dest_ty,
29412 .wanted = src_ty,
29413 } };
29414}
29415
29416fn coerceInMemoryAllowedErrorSets(
29417 sema: *Sema,
29418 block: *Block,
29419 dest_ty: Type,
29420 src_ty: Type,
29421 dest_src: LazySrcLoc,
29422 src_src: LazySrcLoc,
29423) !InMemoryCoercionResult {
29424 const pt = sema.pt;
29425 const zcu = pt.zcu;
29426 const gpa = sema.gpa;
29427 const ip = &zcu.intern_pool;
29428
29429 const dest_set: InternPool.Key.ErrorSetType = err_set: switch (dest_ty.toIntern()) {
29430 .anyerror_type => return .{ .ok = .error_cast },
29431 .adhoc_inferred_error_set_type => {
29432 // We are trying to coerce an error set to the current function's
29433 // inferred error set.
29434 const dst_ies = sema.fn_ret_ty_ies.?;
29435 try dst_ies.addErrorSet(src_ty, ip, sema.arena);
29436 return .{ .ok = .error_cast };
29437 },
29438 else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) {
29439 .inferred_error_set_type => |func_index| {
29440 if (sema.fn_ret_ty_ies) |dst_ies| {
29441 if (dst_ies.func == func_index) {
29442 // We are trying to coerce an error set to the current function's
29443 // inferred error set.
29444 try dst_ies.addErrorSet(src_ty, ip, sema.arena);
29445 return .{ .ok = .error_cast };
29446 }
29447 }
29448 try sema.ensureFuncIesResolved(block, dest_src, func_index);
29449 continue :err_set ip.funcIesResolvedUnordered(func_index);
29450 },
29451 .error_set_type => |err_set| err_set,
29452 else => unreachable,
29453 },
29454 };
29455
29456 const src_names: InternPool.NullTerminatedString.Slice = err_set: switch (src_ty.toIntern()) {
29457 .anyerror_type => return .from_anyerror,
29458 else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) {
29459 .inferred_error_set_type => |func_index| {
29460 try sema.ensureFuncIesResolved(block, src_src, func_index);
29461 continue :err_set ip.funcIesResolvedUnordered(func_index);
29462 },
29463 .error_set_type => |err_set| err_set.names,
29464 else => unreachable,
29465 },
29466 };
29467
29468 var missing_error_buf: std.ArrayList(InternPool.NullTerminatedString) = .empty;
29469 defer missing_error_buf.deinit(gpa);
29470
29471 for (src_names.get(ip)) |name| {
29472 if (dest_set.nameIndex(ip, name) == null) {
29473 try missing_error_buf.append(gpa, name);
29474 }
29475 }
29476
29477 if (missing_error_buf.items.len != 0) {
29478 return .{ .missing_error = try sema.arena.dupe(
29479 InternPool.NullTerminatedString,
29480 missing_error_buf.items,
29481 ) };
29482 }
29483
29484 return .{ .ok = .error_cast };
29485}
29486
29487fn coerceInMemoryAllowedFns(
29488 sema: *Sema,
29489 block: *Block,
29490 dest_ty: Type,
29491 src_ty: Type,
29492 /// If set, the coercion must be valid in both directions.
29493 dest_is_mut: bool,
29494 target: *const std.Target,
29495 dest_src: LazySrcLoc,
29496 src_src: LazySrcLoc,
29497) !InMemoryCoercionResult {
29498 const pt = sema.pt;
29499 const zcu = pt.zcu;
29500 const ip = &zcu.intern_pool;
29501
29502 const dest_info = zcu.typeToFunc(dest_ty).?;
29503 const src_info = zcu.typeToFunc(src_ty).?;
29504
29505 {
29506 if (dest_info.is_var_args != src_info.is_var_args) {
29507 return .{ .fn_var_args = dest_info.is_var_args };
29508 }
29509
29510 const callconv_ok = callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and
29511 (!dest_is_mut or callconvCoerceAllowed(target, dest_info.cc, src_info.cc));
29512
29513 if (!callconv_ok) {
29514 return .{ .fn_cc = .{
29515 .actual = src_info.cc,
29516 .wanted = dest_info.cc,
29517 } };
29518 }
29519
29520 try sema.ensureLayoutResolved(src_ty, src_src, .coerce);
29521 try sema.ensureLayoutResolved(dest_ty, dest_src, .coerce);
29522 const src_is_runtime = src_ty.fnHasRuntimeBits(zcu);
29523 const dest_is_runtime = dest_ty.fnHasRuntimeBits(zcu);
29524 if (src_is_runtime != dest_is_runtime) return .{ .fn_generic = !dest_is_runtime };
29525
29526 if (!switch (src_info.return_type) {
29527 .generic_poison_type => true,
29528 .noreturn_type => !dest_is_mut,
29529 else => false,
29530 }) {
29531 const rt = try sema.coerceInMemoryAllowed(
29532 block,
29533 .fromInterned(dest_info.return_type),
29534 .fromInterned(src_info.return_type),
29535 dest_is_mut,
29536 target,
29537 dest_src,
29538 src_src,
29539 null,
29540 );
29541 if (rt != .ok) return .{ .fn_return_type = .{
29542 .child = try rt.dupe(sema.arena),
29543 .actual = .fromInterned(src_info.return_type),
29544 .wanted = .fromInterned(dest_info.return_type),
29545 } };
29546 }
29547 }
29548
29549 const params_len = params_len: {
29550 if (dest_info.param_types.len != src_info.param_types.len) {
29551 return .{ .fn_param_count = .{
29552 .actual = src_info.param_types.len,
29553 .wanted = dest_info.param_types.len,
29554 } };
29555 }
29556
29557 if (dest_info.noalias_bits != src_info.noalias_bits) {
29558 return .{ .fn_param_noalias = .{
29559 .actual = src_info.noalias_bits,
29560 .wanted = dest_info.noalias_bits,
29561 } };
29562 }
29563
29564 break :params_len dest_info.param_types.len;
29565 };
29566
29567 for (0..params_len) |param_i| {
29568 const dest_param_ty: Type = .fromInterned(dest_info.param_types.get(ip)[param_i]);
29569 const src_param_ty: Type = .fromInterned(src_info.param_types.get(ip)[param_i]);
29570
29571 comptime_param: {
29572 const src_is_comptime = src_info.paramIsComptime(@intCast(param_i));
29573 const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i));
29574 if (src_is_comptime == dest_is_comptime) break :comptime_param;
29575 if (!dest_is_mut and src_is_comptime and !dest_is_comptime and dest_param_ty.comptimeOnly(zcu)) {
29576 // A parameter which is marked `comptime` can drop that annotation if the type is comptime-only.
29577 // The function remains generic, and the parameter is going to be comptime-resolved either way,
29578 // so this just affects whether or not the argument is comptime-evaluated at the call site.
29579 break :comptime_param;
29580 }
29581 return .{ .fn_param_comptime = .{
29582 .index = param_i,
29583 .wanted = dest_is_comptime,
29584 } };
29585 }
29586
29587 if (!src_param_ty.isGenericPoison() and !dest_param_ty.isGenericPoison()) {
29588 // Note: Cast direction is reversed here.
29589 const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, dest_is_mut, target, dest_src, src_src, null);
29590 if (param != .ok) {
29591 return .{ .fn_param = .{
29592 .child = try param.dupe(sema.arena),
29593 .actual = src_param_ty,
29594 .wanted = dest_param_ty,
29595 .index = param_i,
29596 } };
29597 }
29598 }
29599 }
29600
29601 return .{ .ok = .none };
29602}
29603
29604fn callconvCoerceAllowed(
29605 target: *const std.Target,
29606 src_cc: std.lang.CallingConvention,
29607 dest_cc: std.lang.CallingConvention,
29608) bool {
29609 const Tag = std.lang.CallingConvention.Tag;
29610 if (@as(Tag, src_cc) != @as(Tag, dest_cc)) return false;
29611
29612 switch (src_cc) {
29613 inline else => |src_data, tag| {
29614 const dest_data = @field(dest_cc, @tagName(tag));
29615 if (@TypeOf(src_data) != void and @hasField(@TypeOf(src_data), "incoming_stack_alignment")) {
29616 const default_stack_align = target.stackAlignment();
29617 const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align;
29618 const dest_stack_align = dest_data.incoming_stack_alignment orelse default_stack_align;
29619 if (dest_stack_align < src_stack_align) return false;
29620 }
29621 switch (@TypeOf(src_data)) {
29622 void, std.lang.CallingConvention.CommonOptions => {},
29623 std.lang.CallingConvention.X86RegparmOptions => {
29624 if (src_data.register_params != dest_data.register_params) return false;
29625 },
29626 std.lang.CallingConvention.ArcInterruptOptions => {
29627 if (src_data.type != dest_data.type) return false;
29628 },
29629 std.lang.CallingConvention.ArmInterruptOptions => {
29630 if (src_data.type != dest_data.type) return false;
29631 },
29632 std.lang.CallingConvention.MicroblazeInterruptOptions => {
29633 if (src_data.type != dest_data.type) return false;
29634 },
29635 std.lang.CallingConvention.MipsInterruptOptions => {
29636 if (src_data.mode != dest_data.mode) return false;
29637 },
29638 std.lang.CallingConvention.RiscvInterruptOptions => {
29639 if (src_data.mode != dest_data.mode) return false;
29640 },
29641 std.lang.CallingConvention.ShInterruptOptions => {
29642 if (src_data.save != dest_data.save) return false;
29643 },
29644 std.lang.CallingConvention.SpirvKernelOptions,
29645 std.lang.CallingConvention.SpirvFragmentOptions,
29646 std.lang.CallingConvention.SpirvMeshOptions,
29647 => {},
29648 else => comptime unreachable,
29649 }
29650 },
29651 }
29652 return true;
29653}
29654
29655fn coerceInMemoryAllowedPtrs(
29656 sema: *Sema,
29657 block: *Block,
29658 dest_ty: Type,
29659 src_ty: Type,
29660 /// If set, the coercion must be valid in both directions.
29661 dest_is_mut: bool,
29662 target: *const std.Target,
29663 dest_src: LazySrcLoc,
29664 src_src: LazySrcLoc,
29665) !InMemoryCoercionResult {
29666 const pt = sema.pt;
29667 const zcu = pt.zcu;
29668 const comp = zcu.comp;
29669 const gpa = comp.gpa;
29670 const io = comp.io;
29671
29672 const dest_info = dest_ty.ptrInfo(zcu);
29673 const src_info = src_ty.ptrInfo(zcu);
29674
29675 const ok_ptr_size = src_info.flags.size == dest_info.flags.size or
29676 src_info.flags.size == .c or dest_info.flags.size == .c;
29677 if (!ok_ptr_size) {
29678 return .{ .ptr_size = .{
29679 .actual = src_info.flags.size,
29680 .wanted = dest_info.flags.size,
29681 } };
29682 }
29683
29684 const ok_const = src_info.flags.is_const == dest_info.flags.is_const or
29685 (!dest_is_mut and dest_info.flags.is_const);
29686
29687 if (!ok_const) return .{ .ptr_const = .{
29688 .actual = src_ty,
29689 .wanted = dest_ty,
29690 } };
29691
29692 const ok_volatile = src_info.flags.is_volatile == dest_info.flags.is_volatile or
29693 (!dest_is_mut and dest_info.flags.is_volatile);
29694
29695 if (!ok_volatile) return .{ .ptr_volatile = .{
29696 .actual = src_ty,
29697 .wanted = dest_ty,
29698 } };
29699
29700 const dest_allowzero = dest_ty.ptrAllowsZero(zcu);
29701 const src_allowzero = src_ty.ptrAllowsZero(zcu);
29702 const ok_allowzero = src_allowzero == dest_allowzero or
29703 (!dest_is_mut and dest_allowzero);
29704
29705 if (!ok_allowzero) return .{ .ptr_allowzero = .{
29706 .actual = src_ty,
29707 .wanted = dest_ty,
29708 } };
29709
29710 if (dest_info.flags.address_space != src_info.flags.address_space) {
29711 return .{ .ptr_addrspace = .{
29712 .actual = src_info.flags.address_space,
29713 .wanted = dest_info.flags.address_space,
29714 } };
29715 }
29716
29717 const dest_child: Type = .fromInterned(dest_info.child);
29718 const src_child: Type = .fromInterned(src_info.child);
29719 const child = try sema.coerceInMemoryAllowed(
29720 block,
29721 dest_child,
29722 src_child,
29723 // We must also include `dest_is_mut`.
29724 // Otherwise, this code is valid:
29725 //
29726 // const b: B = ...;
29727 // var pa: *const A = undefined;
29728 // const ppa: **const A = &pa;
29729 // const ppb: **const B = ppa; // <-- this is what that allows
29730 // ppb.* = &b;
29731 // const a: A = pa.*;
29732 //
29733 // ...effectively performing an in-memory coercion from B to A.
29734 dest_is_mut or !dest_info.flags.is_const,
29735 target,
29736 dest_src,
29737 src_src,
29738 null,
29739 );
29740 if (child != .ok) allow: {
29741 // As a special case, we also allow coercing `*[n:s]T` to `*[n]T`, akin to dropping the sentinel from a slice.
29742 // `*[n:s]T` cannot coerce in memory to `*[n]T` since they have different sizes.
29743 //
29744 // We must once again include `dest_is_mut` because `**[n:s]T -> **[n]T`
29745 // is not allowed, as it would make it possible to assign an illegal value
29746 // to the sentinel-terminated side.
29747 if (!dest_is_mut and src_child.zigTypeTag(zcu) == .array and dest_child.zigTypeTag(zcu) == .array and
29748 src_child.arrayLen(zcu) == dest_child.arrayLen(zcu) and
29749 src_child.sentinel(zcu) != null and dest_child.sentinel(zcu) == null and
29750 .ok == try sema.coerceInMemoryAllowed(block, dest_child.childType(zcu), src_child.childType(zcu), !dest_info.flags.is_const, target, dest_src, src_src, null))
29751 {
29752 break :allow;
29753 }
29754 return .{ .ptr_child = .{
29755 .child = try child.dupe(sema.arena),
29756 .actual = .fromInterned(src_info.child),
29757 .wanted = .fromInterned(dest_info.child),
29758 } };
29759 }
29760
29761 if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or
29762 src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset)
29763 {
29764 return .{ .ptr_bit_range = .{
29765 .actual_host = src_info.packed_offset.host_size,
29766 .wanted_host = dest_info.packed_offset.host_size,
29767 .actual_offset = src_info.packed_offset.bit_offset,
29768 .wanted_offset = dest_info.packed_offset.bit_offset,
29769 } };
29770 }
29771
29772 const sentinel_ok = ok: {
29773 const ss = src_info.sentinel;
29774 const ds = dest_info.sentinel;
29775 if (ss == .none and ds == .none) break :ok true;
29776 if (ss != .none and ds != .none) {
29777 if (ds == try zcu.intern_pool.getCoerced(gpa, io, pt.tid, ss, dest_info.child)) break :ok true;
29778 }
29779 if (src_info.flags.size == .c) break :ok true;
29780 if (!dest_is_mut and dest_info.sentinel == .none) break :ok true;
29781 break :ok false;
29782 };
29783
29784 if (!sentinel_ok) {
29785 return .{ .ptr_sentinel = .{
29786 .actual = switch (src_info.sentinel) {
29787 .none => Value.@"unreachable",
29788 else => Value.fromInterned(src_info.sentinel),
29789 },
29790 .wanted = switch (dest_info.sentinel) {
29791 .none => Value.@"unreachable",
29792 else => Value.fromInterned(dest_info.sentinel),
29793 },
29794 .ty = .fromInterned(dest_info.child),
29795 } };
29796 }
29797
29798 // If both pointers have alignment 0, it means they both want ABI alignment.
29799 // In this case, if they share the same child type, no need to resolve
29800 // pointee type alignment. Otherwise both pointee types must have their alignment
29801 // resolved and we compare the alignment numerically.
29802 if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or
29803 dest_info.child != src_info.child)
29804 {
29805 const src_align = if (src_info.flags.alignment == .none) a: {
29806 try sema.ensureLayoutResolved(src_child, src_src, .align_check);
29807 break :a src_child.abiAlignment(zcu);
29808 } else src_info.flags.alignment;
29809 const dest_align = if (dest_info.flags.alignment == .none) a: {
29810 try sema.ensureLayoutResolved(dest_child, dest_src, .align_check);
29811 break :a dest_child.abiAlignment(zcu);
29812 } else dest_info.flags.alignment;
29813 if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) {
29814 return .{ .ptr_alignment = .{
29815 .actual = src_align,
29816 .wanted = dest_align,
29817 } };
29818 }
29819 }
29820
29821 return .{ .ok = .ptr_cast };
29822}
29823
29824fn coerceVarArgParam(
29825 sema: *Sema,
29826 block: *Block,
29827 inst: Air.Inst.Ref,
29828 inst_src: LazySrcLoc,
29829) !Air.Inst.Ref {
29830 if (block.is_typeof) return inst;
29831
29832 const pt = sema.pt;
29833 const zcu = pt.zcu;
29834 const uncasted_ty = sema.typeOf(inst);
29835 const coerced = switch (uncasted_ty.zigTypeTag(zcu)) {
29836 // TODO consider casting to c_int/f64 if they fit
29837 .comptime_int, .comptime_float => return sema.fail(
29838 block,
29839 inst_src,
29840 "integer and float literals passed to variadic function must be casted to a fixed-size number type",
29841 .{},
29842 ),
29843 .@"fn" => fn_ptr: {
29844 const fn_val = sema.resolveValue(inst).?;
29845 const fn_nav = zcu.funcInfo(fn_val.toIntern()).owner_nav;
29846 break :fn_ptr try sema.analyzeNavRef(block, inst_src, fn_nav);
29847 },
29848 .array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
29849 .float => float: {
29850 const target = zcu.getTarget();
29851 const double_bits = target.cTypeBitSize(.double) orelse break :float inst;
29852 const inst_bits = uncasted_ty.floatBits(target);
29853 if (inst_bits >= double_bits) break :float inst;
29854 switch (double_bits) {
29855 32 => break :float try sema.coerce(block, .f32, inst, inst_src),
29856 64 => break :float try sema.coerce(block, .f64, inst, inst_src),
29857 else => unreachable,
29858 }
29859 },
29860 else => if (uncasted_ty.isAbiInt(zcu)) int: {
29861 if (!uncasted_ty.validateExtern(.param_ty, zcu)) break :int inst;
29862 const target = zcu.getTarget();
29863 const uncasted_info = uncasted_ty.intInfo(zcu);
29864 if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) {
29865 .signed => .int,
29866 .unsigned => .uint,
29867 }) orelse break :int inst) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
29868 .signed => .c_int,
29869 .unsigned => .c_uint,
29870 }, inst, inst_src);
29871 if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) {
29872 .signed => .long,
29873 .unsigned => .ulong,
29874 }).?) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
29875 .signed => .c_long,
29876 .unsigned => .c_ulong,
29877 }, inst, inst_src);
29878 if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) {
29879 .signed => .longlong,
29880 .unsigned => .ulonglong,
29881 }).?) break :int try sema.coerce(block, switch (uncasted_info.signedness) {
29882 .signed => .c_longlong,
29883 .unsigned => .c_ulonglong,
29884 }, inst, inst_src);
29885 break :int inst;
29886 } else inst,
29887 };
29888
29889 const coerced_ty = sema.typeOf(coerced);
29890 if (!coerced_ty.validateExtern(.param_ty, zcu)) {
29891 const msg = msg: {
29892 const msg = try sema.errMsg(inst_src, "cannot pass '{f}' to variadic function", .{coerced_ty.fmt(pt)});
29893 errdefer msg.destroy(sema.gpa);
29894
29895 try sema.explainWhyTypeIsNotExtern(msg, inst_src, coerced_ty, .param_ty);
29896
29897 try sema.addDeclaredHereNote(msg, coerced_ty);
29898 break :msg msg;
29899 };
29900 return sema.failWithOwnedErrorMsg(block, msg);
29901 }
29902 return coerced;
29903}
29904
29905// TODO migrate callsites to use storePtr2 instead.
29906fn storePtr(
29907 sema: *Sema,
29908 block: *Block,
29909 src: LazySrcLoc,
29910 ptr: Air.Inst.Ref,
29911 uncasted_operand: Air.Inst.Ref,
29912) CompileError!void {
29913 const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store;
29914 return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag);
29915}
29916
29917fn storePtr2(
29918 sema: *Sema,
29919 block: *Block,
29920 src: LazySrcLoc,
29921 ptr: Air.Inst.Ref,
29922 ptr_src: LazySrcLoc,
29923 uncasted_operand: Air.Inst.Ref,
29924 operand_src: LazySrcLoc,
29925 air_tag: Air.Inst.Tag,
29926) CompileError!void {
29927 const pt = sema.pt;
29928 const zcu = pt.zcu;
29929 const ptr_ty = sema.typeOf(ptr);
29930 if (ptr_ty.isConstPtr(zcu))
29931 return sema.fail(block, ptr_src, "cannot assign to constant", .{});
29932
29933 const elem_ty = ptr_ty.childType(zcu);
29934
29935 const is_ret = air_tag == .ret_ptr;
29936
29937 const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) {
29938 error.NotCoercible => unreachable,
29939 else => |e| return e,
29940 };
29941 const maybe_operand_val = sema.resolveValue(operand);
29942
29943 const comptime_only = switch (elem_ty.classify(zcu)) {
29944 .no_possible_value => unreachable, // the coercion should have failed
29945 .one_possible_value => return, // no actual store operation is necessary
29946 .runtime => false,
29947 .partially_comptime, .fully_comptime => true,
29948 };
29949
29950 const runtime_src = rs: {
29951 const ptr_val = try sema.resolveDefinedValue(block, ptr_src, ptr) orelse break :rs ptr_src;
29952 if (!sema.isComptimeMutablePtr(ptr_val)) break :rs ptr_src;
29953 const operand_val = maybe_operand_val orelse return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{});
29954 return sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty);
29955 };
29956
29957 // We're performing the store at runtime, so the pointee type must not be comptime-only.
29958 if (comptime_only) return sema.failWithOwnedErrorMsg(block, msg: {
29959 const msg = try sema.errMsg(src, "cannot store comptime-only type '{f}' at runtime", .{elem_ty.fmt(pt)});
29960 errdefer msg.destroy(zcu.gpa);
29961 try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{});
29962 break :msg msg;
29963 });
29964
29965 try sema.requireRuntimeBlock(block, src, runtime_src);
29966
29967 const store_inst = if (is_ret)
29968 try block.addBinOp(.store, ptr, operand)
29969 else
29970 try block.addBinOp(air_tag, ptr, operand);
29971
29972 try sema.checkComptimeKnownStore(block, store_inst, operand_src);
29973
29974 return;
29975}
29976
29977/// Given an AIR store instruction, checks whether we are performing a
29978/// comptime-known store to a local alloc, and updates `maybe_comptime_allocs`
29979/// accordingly.
29980/// Handles calling `validateRuntimeValue` if the store is runtime for any reason.
29981fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst.Ref, store_src: LazySrcLoc) !void {
29982 const store_inst = store_inst_ref.toIndex().?;
29983 const inst_data = sema.air_instructions.items(.data)[@backingInt(store_inst)].bin_op;
29984 const operand = inst_data.rhs;
29985
29986 known: {
29987 const ptr = inst_data.lhs.toIndex() orelse {
29988 const ptr_val: Value = .fromInterned(inst_data.lhs.toInterned().?);
29989 if (sema.isComptimeMutablePtr(ptr_val)) {
29990 return;
29991 } else {
29992 break :known;
29993 }
29994 };
29995
29996 const maybe_base_alloc = sema.base_allocs.get(ptr) orelse break :known;
29997 const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse break :known;
29998
29999 if (sema.resolveValue(operand) != null and
30000 block.runtime_index == maybe_comptime_alloc.runtime_index)
30001 {
30002 try maybe_comptime_alloc.stores.append(sema.arena, .{
30003 .inst = store_inst,
30004 .src = store_src,
30005 });
30006 return;
30007 }
30008
30009 // We're newly discovering that this alloc is runtime-known.
30010 try sema.markMaybeComptimeAllocRuntime(block, maybe_base_alloc);
30011 }
30012
30013 try sema.validateRuntimeValue(block, store_src, operand);
30014}
30015
30016/// Given an AIR instruction transforming a pointer (struct_field_ptr,
30017/// ptr_elem_ptr, bitcast, etc), checks whether the base pointer refers to a
30018/// local alloc, and updates `base_allocs` accordingly.
30019fn checkKnownAllocPtr(sema: *Sema, block: *Block, base_ptr: Air.Inst.Ref, new_ptr: Air.Inst.Ref) !void {
30020 const base_ptr_inst = base_ptr.toIndex() orelse return;
30021 const new_ptr_inst = new_ptr.toIndex() orelse return;
30022 const alloc_inst = sema.base_allocs.get(base_ptr_inst) orelse return;
30023 try sema.base_allocs.put(sema.gpa, new_ptr_inst, alloc_inst);
30024
30025 switch (sema.air_instructions.items(.tag)[@backingInt(new_ptr_inst)]) {
30026 .optional_payload_ptr_set, .errunion_payload_ptr_set => {
30027 const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(alloc_inst) orelse return;
30028
30029 // This is functionally a store, since it writes the optional payload bit.
30030 // Thus, if it is behind a runtime condition, we must mark the alloc as runtime appropriately.
30031 if (block.runtime_index != maybe_comptime_alloc.runtime_index) {
30032 return sema.markMaybeComptimeAllocRuntime(block, alloc_inst);
30033 }
30034
30035 try maybe_comptime_alloc.stores.append(sema.arena, .{
30036 .inst = new_ptr_inst,
30037 .src = LazySrcLoc.unneeded,
30038 });
30039 },
30040 .ptr_elem_ptr => {
30041 const tmp_air = sema.getTmpAir();
30042 const pl_idx = tmp_air.instructions.items(.data)[@backingInt(new_ptr_inst)].ty_pl.payload;
30043 const bin = tmp_air.extraData(Air.Bin, pl_idx).data;
30044 const index_ref = bin.rhs;
30045
30046 // If the index value is runtime-known, this pointer is also runtime-known, so
30047 // we must in turn make the alloc value runtime-known.
30048 if (null == sema.resolveValue(index_ref)) {
30049 try sema.markMaybeComptimeAllocRuntime(block, alloc_inst);
30050 }
30051 },
30052 else => {},
30053 }
30054}
30055
30056fn markMaybeComptimeAllocRuntime(sema: *Sema, block: *Block, alloc_inst: Air.Inst.Index) CompileError!void {
30057 const maybe_comptime_alloc = (sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return).value;
30058 // Since the alloc has been determined to be runtime, we must check that
30059 // all other stores to it are permitted to be runtime values.
30060 const slice = maybe_comptime_alloc.stores.slice();
30061 for (slice.items(.inst), slice.items(.src)) |other_inst, other_src| {
30062 if (other_src.offset == .unneeded) {
30063 switch (sema.air_instructions.items(.tag)[@backingInt(other_inst)]) {
30064 .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => continue,
30065 else => unreachable, // assertion failure
30066 }
30067 }
30068 const other_data = sema.air_instructions.items(.data)[@backingInt(other_inst)].bin_op;
30069 const other_operand = other_data.rhs;
30070 try sema.validateRuntimeValue(block, other_src, other_operand);
30071 }
30072}
30073
30074/// Call when you have Value objects rather than Air instructions, and you want to
30075/// assert the store must be done at comptime.
30076fn storePtrVal(
30077 sema: *Sema,
30078 block: *Block,
30079 src: LazySrcLoc,
30080 ptr_val: Value,
30081 operand_val: Value,
30082 operand_ty: Type,
30083) !void {
30084 const pt = sema.pt;
30085 const zcu = pt.zcu;
30086 const ip = &zcu.intern_pool;
30087 // TODO: audit use sites to eliminate this coercion
30088 const coerced_operand_val = try pt.getCoerced(operand_val, operand_ty);
30089 // TODO: audit use sites to eliminate this coercion
30090 const ptr_ty = try pt.ptrType(info: {
30091 var info = ptr_val.typeOf(zcu).ptrInfo(zcu);
30092 info.child = operand_ty.toIntern();
30093 break :info info;
30094 });
30095 const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty);
30096
30097 switch (try sema.storeComptimePtr(block, src, coerced_ptr_val, coerced_operand_val)) {
30098 .success => {},
30099 .runtime_store => unreachable, // use sites check this
30100 // TODO use failWithInvalidComptimeFieldStore
30101 .comptime_field_mismatch => return sema.fail(
30102 block,
30103 src,
30104 "value stored in comptime field does not match the default value of the field",
30105 .{},
30106 ),
30107 .undef => return sema.failWithUseOfUndef(block, src, null),
30108 .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}),
30109 .null_payload => return sema.fail(block, src, "attempt to use null value", .{}),
30110 .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}),
30111 .needed_well_defined => |ty| return sema.fail(
30112 block,
30113 src,
30114 "comptime dereference requires '{f}' to have a well-defined layout",
30115 .{ty.fmt(pt)},
30116 ),
30117 .out_of_bounds => |ty| return sema.fail(
30118 block,
30119 src,
30120 "dereference of '{f}' exceeds bounds of containing decl of type '{f}'",
30121 .{ ptr_ty.fmt(pt), ty.fmt(pt) },
30122 ),
30123 .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}),
30124 }
30125}
30126
30127/// Asserts that the layout of `dest_ty` is already resolved.
30128fn bitCastUnchecked(
30129 sema: *Sema,
30130 block: *Block,
30131 dest_ty: Type,
30132 inst: Air.Inst.Ref,
30133) CompileError!Air.Inst.Ref {
30134 const zcu = sema.pt.zcu;
30135 const old_ty = sema.typeOf(inst);
30136
30137 old_ty.assertHasLayout(zcu);
30138 dest_ty.assertHasLayout(zcu);
30139
30140 assert(old_ty.hasBitRepresentation(zcu));
30141 assert(dest_ty.hasBitRepresentation(zcu));
30142 assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
30143 assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer);
30144 assert(old_ty.bitSize(zcu) == dest_ty.bitSize(zcu));
30145
30146 if (sema.resolveValue(inst)) |val| {
30147 return .fromValue(try sema.bitCastVal(val, dest_ty));
30148 }
30149
30150 return block.addTyOp(.bit_cast, dest_ty, inst);
30151}
30152
30153/// Supports only types which `@bitCast` supports, so pointers are *not* supported.
30154pub fn bitCastVal(
30155 sema: *Sema,
30156 val: Value,
30157 dest_ty: Type,
30158) Allocator.Error!Value {
30159 const pt = sema.pt;
30160 const zcu = pt.zcu;
30161 const bit_size = dest_ty.bitSize(zcu);
30162 assert(val.typeOf(zcu).bitSize(zcu) == bit_size);
30163 if (val.isUndef(zcu)) {
30164 return pt.undefValue(dest_ty);
30165 } else {
30166 const buf = try sema.arena.alloc(u8, @intCast(@divCeil(bit_size, 8)));
30167 @memset(buf, 0);
30168 val.writeToPackedMemory(zcu, buf, 0);
30169 return .readFromPackedMemory(dest_ty, pt, buf, 0);
30170 }
30171}
30172
30173fn errorCastUnchecked(
30174 sema: *Sema,
30175 block: *Block,
30176 dest_ty: Type,
30177 inst: Air.Inst.Ref,
30178) CompileError!Air.Inst.Ref {
30179 const pt = sema.pt;
30180 const zcu = pt.zcu;
30181 assert(dest_ty.zigTypeTag(zcu) == .error_set);
30182 assert(sema.typeOf(inst).zigTypeTag(zcu) == .error_set);
30183 if (sema.resolveValue(inst)) |val| {
30184 if (val.isUndef(zcu)) return pt.undefRef(dest_ty);
30185 return .fromIntern(try pt.intern(.{ .err = .{
30186 .ty = dest_ty.toIntern(),
30187 .name = zcu.intern_pool.indexToKey(val.toIntern()).err.name,
30188 } }));
30189 }
30190 return block.addTyOp(.error_cast, dest_ty, inst);
30191}
30192
30193fn checkSpirvSliceAllowed(
30194 sema: *Sema,
30195 block: *Block,
30196 src: LazySrcLoc,
30197 address_space: std.lang.AddressSpace,
30198) CompileError!void {
30199 const zcu = sema.pt.zcu;
30200 const target = zcu.getTarget();
30201
30202 if (!target.cpu.arch.isSpirV()) return;
30203 if (block.isComptime()) return;
30204
30205 // This probably lets some invalid OpPtrAccessChains slip through, but it's better than nothing
30206 if (!target.cpu.has(.spirv, .variable_pointers) and !target.cpu.has(.spirv, .variable_pointers_storage_buffer)) {
30207 return sema.failWithOwnedErrorMsg(
30208 block,
30209 try sema.errMsg(src, "cannot construct slices without the 'variable_pointers' or 'variable_pointers_storage_buffer' features", .{}),
30210 );
30211 }
30212
30213 switch (address_space) {
30214 .shared, .storage_buffer => {},
30215 else => {
30216 return sema.failWithOwnedErrorMsg(block, msg: {
30217 const msg = try sema.errMsg(src, "cannot construct slice from address space '{t}'", .{address_space});
30218 errdefer msg.destroy(sema.gpa);
30219 try sema.errNote(src, msg, "only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V", .{});
30220 break :msg msg;
30221 });
30222 },
30223 }
30224}
30225
30226fn coerceArrayPtrToSlice(
30227 sema: *Sema,
30228 block: *Block,
30229 dest_ty: Type,
30230 inst: Air.Inst.Ref,
30231 inst_src: LazySrcLoc,
30232) CompileError!Air.Inst.Ref {
30233 const pt = sema.pt;
30234 const zcu = pt.zcu;
30235 if (sema.resolveValue(inst)) |val| {
30236 const ptr_array_ty = sema.typeOf(inst);
30237 const array_ty = ptr_array_ty.childType(zcu);
30238 const slice_ptr_ty = dest_ty.slicePtrFieldType(zcu);
30239 const slice_ptr = try pt.getCoerced(val, slice_ptr_ty);
30240 const slice_val = try pt.intern(.{ .slice = .{
30241 .ty = dest_ty.toIntern(),
30242 .ptr = slice_ptr.toIntern(),
30243 .len = (try pt.intValue(.usize, array_ty.arrayLen(zcu))).toIntern(),
30244 } });
30245 return Air.internedToRef(slice_val);
30246 }
30247 try sema.checkSpirvSliceAllowed(block, inst_src, dest_ty.ptrInfo(zcu).flags.address_space);
30248 try sema.requireRuntimeBlock(block, inst_src, null);
30249 return block.addTyOp(.array_to_slice, dest_ty, inst);
30250}
30251
30252fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool {
30253 const pt = sema.pt;
30254 const zcu = pt.zcu;
30255 const dest_info = dest_ty.ptrInfo(zcu);
30256 const inst_info = inst_ty.ptrInfo(zcu);
30257 const len0 = (Type.fromInterned(inst_info.child).zigTypeTag(zcu) == .array and (Type.fromInterned(inst_info.child).arrayLenIncludingSentinel(zcu) == 0 or
30258 (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
30259 (Type.fromInterned(inst_info.child).isTuple(zcu) and Type.fromInterned(inst_info.child).structFieldCount(zcu) == 0);
30260
30261 const ok_const = (!inst_info.flags.is_const or dest_info.flags.is_const) or len0;
30262 const ok_volatile = !inst_info.flags.is_volatile or dest_info.flags.is_volatile;
30263 if (!ok_const) {
30264 in_memory_result.* = .{ .ptr_const = .{
30265 .actual = inst_ty,
30266 .wanted = dest_ty,
30267 } };
30268 return false;
30269 }
30270 if (!ok_volatile) {
30271 in_memory_result.* = .{ .ptr_volatile = .{
30272 .actual = inst_ty,
30273 .wanted = dest_ty,
30274 } };
30275 return false;
30276 }
30277
30278 if (dest_info.flags.address_space != inst_info.flags.address_space) {
30279 in_memory_result.* = .{ .ptr_addrspace = .{
30280 .actual = inst_info.flags.address_space,
30281 .wanted = dest_info.flags.address_space,
30282 } };
30283 return false;
30284 }
30285
30286 if (inst_info.packed_offset.host_size != dest_info.packed_offset.host_size or
30287 inst_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset)
30288 {
30289 in_memory_result.* = .{ .ptr_bit_range = .{
30290 .actual_host = inst_info.packed_offset.host_size,
30291 .wanted_host = dest_info.packed_offset.host_size,
30292 .actual_offset = inst_info.packed_offset.bit_offset,
30293 .wanted_offset = dest_info.packed_offset.bit_offset,
30294 } };
30295 return false;
30296 }
30297
30298 if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true;
30299 if (len0) return true;
30300
30301 const inst_align = if (inst_info.flags.alignment != .none)
30302 inst_info.flags.alignment
30303 else
30304 Type.fromInterned(inst_info.child).abiAlignment(zcu);
30305
30306 const dest_align = if (dest_info.flags.alignment != .none)
30307 dest_info.flags.alignment
30308 else
30309 Type.fromInterned(dest_info.child).abiAlignment(zcu);
30310
30311 if (dest_align.compare(.gt, inst_align)) {
30312 in_memory_result.* = .{ .ptr_alignment = .{
30313 .actual = inst_align,
30314 .wanted = dest_align,
30315 } };
30316 return false;
30317 }
30318 return true;
30319}
30320
30321fn coerceCompatiblePtrs(
30322 sema: *Sema,
30323 block: *Block,
30324 dest_ty: Type,
30325 inst: Air.Inst.Ref,
30326 inst_src: LazySrcLoc,
30327) !Air.Inst.Ref {
30328 const pt = sema.pt;
30329 const zcu = pt.zcu;
30330 const inst_ty = sema.typeOf(inst);
30331 if (sema.resolveValue(inst)) |val| {
30332 if (!val.isUndef(zcu) and val.isNull(zcu) and !dest_ty.isAllowzeroPtr(zcu)) {
30333 return sema.fail(block, inst_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)});
30334 }
30335 // The comptime Value representation is compatible with both types.
30336 return Air.internedToRef(
30337 (try pt.getCoerced(val, dest_ty)).toIntern(),
30338 );
30339 }
30340 try sema.requireRuntimeBlock(block, inst_src, null);
30341 const maybe_zero: bool = switch (inst_ty.toIntern()) {
30342 .usize_type, .isize_type => true,
30343 else => inst_ty.ptrAllowsZero(zcu),
30344 };
30345 if (block.wantSafety() and maybe_zero and !dest_ty.ptrAllowsZero(zcu)) {
30346 try sema.checkLogicalPtrOperation(block, inst_src, inst_ty);
30347 const actual_ptr = if (inst_ty.isSlice(zcu))
30348 try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty)
30349 else
30350 inst;
30351 const ptr_int = try block.addTyOp(.int_from_ptr, .usize, actual_ptr);
30352 const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize);
30353 const ok = if (inst_ty.isSlice(zcu)) ok: {
30354 const len = try sema.analyzeSliceLen(block, inst_src, inst);
30355 const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize);
30356 break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero);
30357 } else is_non_zero;
30358 try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null);
30359 }
30360 const new_ptr: Air.Inst.Ref = switch (inst_ty.toIntern()) {
30361 .usize_type => try block.addTyOp(.ptr_from_int, dest_ty, inst),
30362 .isize_type => new_ptr: {
30363 const usize_inst = try block.addTyOp(.bit_cast, .usize, inst);
30364 break :new_ptr try block.addTyOp(.ptr_from_int, dest_ty, usize_inst);
30365 },
30366 else => try block.addTyOp(.ptr_cast, dest_ty, inst),
30367 };
30368 try sema.checkKnownAllocPtr(block, inst, new_ptr);
30369 return new_ptr;
30370}
30371
30372/// Asserts that the layout of `union_ty` is already resolved.
30373fn coerceEnumToUnion(
30374 sema: *Sema,
30375 block: *Block,
30376 union_ty: Type,
30377 union_ty_src: LazySrcLoc,
30378 inst: Air.Inst.Ref,
30379 inst_src: LazySrcLoc,
30380) !Air.Inst.Ref {
30381 const pt = sema.pt;
30382 const zcu = pt.zcu;
30383 const ip = &zcu.intern_pool;
30384 const inst_ty = sema.typeOf(inst);
30385
30386 union_ty.assertHasLayout(zcu);
30387
30388 const union_obj = zcu.typeToUnion(union_ty).?;
30389 const enum_ty: Type = .fromInterned(union_obj.enum_tag_type);
30390 const enum_obj = ip.loadEnumType(enum_ty.toIntern());
30391
30392 if (union_obj.tag_usage != .tagged) return sema.failWithOwnedErrorMsg(block, msg: {
30393 const msg = try sema.typeMismatchErrMsg(inst_src, union_ty, inst_ty);
30394 errdefer msg.destroy(sema.gpa);
30395 try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{});
30396 try sema.addDeclaredHereNote(msg, union_ty);
30397 break :msg msg;
30398 });
30399
30400 const enum_tag = try sema.coerce(block, enum_ty, inst, inst_src);
30401 if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
30402 const field_index = union_ty.unionTagFieldIndex(val, pt.zcu) orelse {
30403 return sema.fail(block, inst_src, "union '{f}' has no tag with value '{f}'", .{
30404 union_ty.fmt(pt), val.fmtValueSema(pt, sema),
30405 });
30406 };
30407
30408 const field_name = enum_obj.field_names.get(ip)[field_index];
30409 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
30410 switch (field_ty.classify(zcu)) {
30411 .one_possible_value => return .fromValue(try pt.unionValue(
30412 union_ty,
30413 val,
30414 (try field_ty.onePossibleValue(pt)).?,
30415 )),
30416
30417 .no_possible_value => return sema.failWithOwnedErrorMsg(block, msg: {
30418 const msg = try sema.errMsg(inst_src, "cannot initialize union field with uninstantiable type '{f}'", .{field_ty.fmt(pt)});
30419 errdefer msg.destroy(sema.gpa);
30420 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{
30421 field_name.fmt(ip),
30422 });
30423 try sema.addDeclaredHereNote(msg, union_ty);
30424 break :msg msg;
30425 }),
30426
30427 else => return sema.failWithOwnedErrorMsg(block, msg: {
30428 const msg = try sema.errMsg(inst_src, "coercion from enum '{f}' to union '{f}' must initialize '{f}' field '{f}'", .{
30429 inst_ty.fmt(pt), union_ty.fmt(pt),
30430 field_ty.fmt(pt), field_name.fmt(ip),
30431 });
30432 errdefer msg.destroy(sema.gpa);
30433
30434 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{field_name.fmt(ip)});
30435 try sema.addDeclaredHereNote(msg, union_ty);
30436 break :msg msg;
30437 }),
30438 }
30439 }
30440
30441 try sema.requireRuntimeBlock(block, inst_src, null);
30442
30443 if (enum_ty.isNonexhaustiveEnum(zcu)) {
30444 const msg = msg: {
30445 const msg = try sema.errMsg(inst_src, "runtime coercion to union '{f}' from non-exhaustive enum", .{
30446 union_ty.fmt(pt),
30447 });
30448 errdefer msg.destroy(sema.gpa);
30449 try sema.addDeclaredHereNote(msg, enum_ty);
30450 break :msg msg;
30451 };
30452 return sema.failWithOwnedErrorMsg(block, msg);
30453 }
30454
30455 for (union_obj.field_types.get(ip)) |field_ty_ip| {
30456 if (Type.fromInterned(field_ty_ip).classify(zcu) != .one_possible_value) break;
30457 } else {
30458 // All fields are OPV, so the coercion is okay.
30459 if (try union_ty.onePossibleValue(pt)) |opv| {
30460 // The tag had redundant bits, but we've omitted the tag from the union's runtime layout, so the union is OPV and hence runtime-known.
30461 return .fromValue(opv);
30462 } else {
30463 // The union layout is just the tag, so we can bitcast the enum straight to the union.
30464 return block.addTyOp(.union_from_enum, union_ty, enum_tag);
30465 }
30466 }
30467
30468 // The coercion is invalid because one or more fields is not OPV.
30469
30470 const msg = msg: {
30471 const msg = try sema.errMsg(
30472 inst_src,
30473 "runtime coercion from enum '{f}' to union '{f}' which has non-void fields",
30474 .{ enum_ty.fmt(pt), union_ty.fmt(pt) },
30475 );
30476 errdefer msg.destroy(sema.gpa);
30477
30478 for (0..union_obj.field_types.len) |field_index| {
30479 const field_name = enum_obj.field_names.get(ip)[field_index];
30480 const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]);
30481 const ty_description: []const u8 = switch (field_ty.classify(zcu)) {
30482 .one_possible_value => continue,
30483 .no_possible_value => "uninstantiable type",
30484 else => "type",
30485 };
30486 if (field_ty.classify(zcu) == .one_possible_value) continue;
30487 try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' has {s} '{f}'", .{
30488 field_name.fmt(ip),
30489 ty_description,
30490 field_ty.fmt(pt),
30491 });
30492 }
30493 try sema.addDeclaredHereNote(msg, union_ty);
30494 break :msg msg;
30495 };
30496 return sema.failWithOwnedErrorMsg(block, msg);
30497}
30498
30499/// If the lengths match, coerces element-wise.
30500fn coerceArrayLike(
30501 sema: *Sema,
30502 block: *Block,
30503 dest_ty: Type,
30504 dest_ty_src: LazySrcLoc,
30505 inst: Air.Inst.Ref,
30506 inst_src: LazySrcLoc,
30507) !Air.Inst.Ref {
30508 const pt = sema.pt;
30509 const zcu = pt.zcu;
30510 const inst_ty = sema.typeOf(inst);
30511 const target = zcu.getTarget();
30512
30513 const inst_len = inst_ty.arrayLen(zcu);
30514 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu));
30515 if (dest_len != inst_len) {
30516 const msg = msg: {
30517 const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty);
30518 errdefer msg.destroy(sema.gpa);
30519 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
30520 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
30521 break :msg msg;
30522 };
30523 return sema.failWithOwnedErrorMsg(block, msg);
30524 }
30525
30526 const dest_elem_ty = dest_ty.childType(zcu);
30527 if (dest_ty.isVector(zcu) and inst_ty.isVector(zcu) and sema.resolveValue(inst) == null) {
30528 const inst_elem_ty = inst_ty.childType(zcu);
30529 switch (dest_elem_ty.zigTypeTag(zcu)) {
30530 .int => if (inst_elem_ty.isInt(zcu)) {
30531 // integer widening
30532 const dst_info = dest_elem_ty.intInfo(zcu);
30533 const src_info = inst_elem_ty.intInfo(zcu);
30534 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
30535 // small enough unsigned ints can get casted to large enough signed ints
30536 (dst_info.signedness == .signed and dst_info.bits > src_info.bits))
30537 {
30538 try sema.requireRuntimeBlock(block, inst_src, null);
30539 return block.addTyOp(.int_cast, dest_ty, inst);
30540 }
30541 },
30542 .float => if (inst_elem_ty.isRuntimeFloat()) {
30543 // float widening
30544 const src_bits = inst_elem_ty.floatBits(target);
30545 const dst_bits = dest_elem_ty.floatBits(target);
30546 if (dst_bits >= src_bits) {
30547 try sema.requireRuntimeBlock(block, inst_src, null);
30548 return block.addTyOp(.fpext, dest_ty, inst);
30549 }
30550 },
30551 else => {},
30552 }
30553 }
30554
30555 // Matching element types means no per-element work, so let the backend lower the conversion.
30556 if (dest_ty.isVector(zcu) and
30557 inst_ty.zigTypeTag(zcu) == .array and
30558 inst_ty.childType(zcu).toIntern() == dest_elem_ty.toIntern() and
30559 sema.resolveValue(inst) == null)
30560 {
30561 try sema.requireRuntimeBlock(block, inst_src, null);
30562 return block.addTyOp(.array_to_vector, dest_ty, inst);
30563 }
30564
30565 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
30566 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
30567 var runtime_src: ?LazySrcLoc = null;
30568
30569 for (element_vals, element_refs, 0..) |*val, *ref, i| {
30570 const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern());
30571 const src = inst_src; // TODO better source location
30572 const elem_src = inst_src; // TODO better source location
30573 const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true);
30574 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
30575 ref.* = coerced;
30576 if (runtime_src == null) {
30577 if (sema.resolveValue(coerced)) |elem_val| {
30578 val.* = elem_val.toIntern();
30579 } else {
30580 runtime_src = elem_src;
30581 }
30582 }
30583 }
30584
30585 if (runtime_src) |rs| {
30586 try sema.requireRuntimeBlock(block, inst_src, rs);
30587 return block.addAggregateInit(dest_ty, element_refs);
30588 }
30589
30590 return Air.internedToRef((try pt.aggregateValue(dest_ty, element_vals)).toIntern());
30591}
30592
30593/// If the lengths match, coerces element-wise.
30594fn coerceTupleToArray(
30595 sema: *Sema,
30596 block: *Block,
30597 dest_ty: Type,
30598 dest_ty_src: LazySrcLoc,
30599 inst: Air.Inst.Ref,
30600 inst_src: LazySrcLoc,
30601) !Air.Inst.Ref {
30602 const pt = sema.pt;
30603 const zcu = pt.zcu;
30604 const inst_ty = sema.typeOf(inst);
30605 const inst_len = inst_ty.arrayLen(zcu);
30606 const dest_len = dest_ty.arrayLen(zcu);
30607
30608 if (dest_len != inst_len) {
30609 const msg = msg: {
30610 const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty);
30611 errdefer msg.destroy(sema.gpa);
30612 try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len});
30613 try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len});
30614 break :msg msg;
30615 };
30616 return sema.failWithOwnedErrorMsg(block, msg);
30617 }
30618
30619 const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len);
30620 const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems);
30621 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems);
30622 const dest_elem_ty = dest_ty.childType(zcu);
30623
30624 var runtime_src: ?LazySrcLoc = null;
30625 for (element_vals, element_refs, 0..) |*val, *ref, i_usize| {
30626 const i: u32 = @intCast(i_usize);
30627 if (i_usize == inst_len) {
30628 const sentinel_val = dest_ty.sentinel(zcu).?;
30629 val.* = sentinel_val.toIntern();
30630 ref.* = Air.internedToRef(sentinel_val.toIntern());
30631 break;
30632 }
30633 const elem_src = inst_src; // TODO better source location
30634 const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i);
30635 const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src);
30636 ref.* = coerced;
30637 if (runtime_src == null) {
30638 if (sema.resolveValue(coerced)) |elem_val| {
30639 val.* = elem_val.toIntern();
30640 } else {
30641 runtime_src = elem_src;
30642 }
30643 }
30644 }
30645
30646 if (runtime_src) |rs| {
30647 try sema.requireRuntimeBlock(block, inst_src, rs);
30648 return block.addAggregateInit(dest_ty, element_refs);
30649 }
30650
30651 return Air.internedToRef((try pt.aggregateValue(dest_ty, element_vals)).toIntern());
30652}
30653
30654/// If the lengths match, coerces element-wise.
30655fn coerceTupleToSlicePtrs(
30656 sema: *Sema,
30657 block: *Block,
30658 slice_ty: Type,
30659 slice_ty_src: LazySrcLoc,
30660 ptr_tuple: Air.Inst.Ref,
30661 tuple_src: LazySrcLoc,
30662) !Air.Inst.Ref {
30663 const pt = sema.pt;
30664 const zcu = pt.zcu;
30665 const tuple_ty = sema.typeOf(ptr_tuple).childType(zcu);
30666 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
30667 const slice_info = slice_ty.ptrInfo(zcu);
30668 const array_ty = try pt.arrayType(.{
30669 .len = tuple_ty.structFieldCount(zcu),
30670 .sentinel = slice_info.sentinel,
30671 .child = slice_info.child,
30672 });
30673 const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
30674 const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst, slice_info.flags.alignment);
30675 return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
30676}
30677
30678/// If the lengths match, coerces element-wise.
30679fn coerceTupleToArrayPtrs(
30680 sema: *Sema,
30681 block: *Block,
30682 ptr_array_ty: Type,
30683 array_ty_src: LazySrcLoc,
30684 ptr_tuple: Air.Inst.Ref,
30685 tuple_src: LazySrcLoc,
30686) !Air.Inst.Ref {
30687 const pt = sema.pt;
30688 const zcu = pt.zcu;
30689 const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
30690 const ptr_info = ptr_array_ty.ptrInfo(zcu);
30691 const array_ty: Type = .fromInterned(ptr_info.child);
30692 const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src);
30693 const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst, ptr_info.flags.alignment);
30694 return ptr_array;
30695}
30696
30697fn coerceTupleToTuple(
30698 sema: *Sema,
30699 block: *Block,
30700 tuple_ty: Type,
30701 inst: Air.Inst.Ref,
30702 inst_src: LazySrcLoc,
30703) !Air.Inst.Ref {
30704 const pt = sema.pt;
30705 const zcu = pt.zcu;
30706 const ip = &zcu.intern_pool;
30707 const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) {
30708 .tuple_type => |tuple_type| tuple_type.types.len,
30709 else => unreachable,
30710 };
30711 const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count);
30712 const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
30713 @memset(field_refs, .none);
30714
30715 const inst_ty = sema.typeOf(inst);
30716 const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) {
30717 .tuple_type => |tuple_type| tuple_type.types.len,
30718 else => unreachable,
30719 };
30720 if (src_field_count > dest_field_count) return error.NotCoercible;
30721
30722 var runtime_src: ?LazySrcLoc = null;
30723 for (0..dest_field_count) |field_index_usize| {
30724 const field_i: u32 = @intCast(field_index_usize);
30725 const field_src = inst_src; // TODO better source location
30726
30727 const field_index: u32 = @intCast(field_index_usize);
30728
30729 const field_ty, const default_val = field: {
30730 const tuple_type = ip.indexToKey(tuple_ty.toIntern()).tuple_type;
30731 break :field .{
30732 tuple_type.types.get(ip)[field_index],
30733 tuple_type.values.get(ip)[field_index],
30734 };
30735 };
30736
30737 const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i);
30738 const coerced = try sema.coerce(block, .fromInterned(field_ty), elem_ref, field_src);
30739 field_refs[field_index] = coerced;
30740 if (default_val != .none) {
30741 const init_val = sema.resolveValue(coerced) orelse {
30742 return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field });
30743 };
30744
30745 if (!init_val.eql(Value.fromInterned(default_val), .fromInterned(field_ty), pt.zcu)) {
30746 return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i);
30747 }
30748 }
30749 if (runtime_src == null) {
30750 if (sema.resolveValue(coerced)) |field_val| {
30751 field_vals[field_index] = field_val.toIntern();
30752 } else {
30753 runtime_src = field_src;
30754 }
30755 }
30756 }
30757
30758 // Populate default field values and report errors for missing fields.
30759 var root_msg: ?*Zcu.ErrorMsg = null;
30760 errdefer if (root_msg) |msg| msg.destroy(sema.gpa);
30761
30762 for (field_refs, 0..) |*field_ref, i_usize| {
30763 const i: u32 = @intCast(i_usize);
30764 if (field_ref.* != .none) continue;
30765
30766 const default_val = ip.indexToKey(tuple_ty.toIntern()).tuple_type.values.get(ip)[i];
30767
30768 const field_src = inst_src; // TODO better source location
30769 if (default_val == .none) {
30770 const template = "missing tuple field: {d}";
30771 if (root_msg) |msg| {
30772 try sema.errNote(field_src, msg, template, .{i});
30773 } else {
30774 root_msg = try sema.errMsg(field_src, template, .{i});
30775 }
30776 continue;
30777 }
30778 if (runtime_src == null) {
30779 field_vals[i] = default_val;
30780 } else {
30781 field_ref.* = Air.internedToRef(default_val);
30782 }
30783 }
30784
30785 if (root_msg) |msg| {
30786 try sema.addDeclaredHereNote(msg, tuple_ty);
30787 root_msg = null;
30788 return sema.failWithOwnedErrorMsg(block, msg);
30789 }
30790
30791 if (runtime_src) |rs| {
30792 try sema.requireRuntimeBlock(block, inst_src, rs);
30793 return block.addAggregateInit(tuple_ty, field_refs);
30794 }
30795
30796 return Air.internedToRef((try pt.aggregateValue(tuple_ty, field_vals)).toIntern());
30797}
30798
30799fn analyzeNavVal(
30800 sema: *Sema,
30801 block: *Block,
30802 src: LazySrcLoc,
30803 nav_index: InternPool.Nav.Index,
30804) CompileError!Air.Inst.Ref {
30805 const ref = try sema.analyzeNavRefInner(block, src, nav_index, false);
30806 return sema.analyzeLoad(block, src, ref, src);
30807}
30808
30809pub fn addReferenceEntry(
30810 sema: *Sema,
30811 opt_block: ?*Block,
30812 src: LazySrcLoc,
30813 referenced_unit: AnalUnit,
30814) !void {
30815 const zcu = sema.pt.zcu;
30816 const ip = &zcu.intern_pool;
30817 switch (referenced_unit.unwrap()) {
30818 .func => |f| assert(ip.unwrapCoercedFunc(f) == f), // for `.{ .func = f }`, `f` must be uncoerced
30819 else => {},
30820 }
30821 const gop = try sema.references.getOrPut(sema.gpa, referenced_unit);
30822 if (gop.found_existing) return;
30823 try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: {
30824 const block = opt_block orelse break :inline_frame .none;
30825 const inlining = block.inlining orelse break :inline_frame .none;
30826 const frame = try inlining.refFrame(zcu);
30827 break :inline_frame frame.toOptional();
30828 });
30829}
30830
30831pub fn addTypeReferenceEntry(
30832 sema: *Sema,
30833 src: LazySrcLoc,
30834 referenced_type: Type,
30835) !void {
30836 const zcu = sema.pt.zcu;
30837 const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type.toIntern());
30838 if (gop.found_existing) return;
30839 try zcu.addTypeReference(sema.owner, referenced_type.toIntern(), src);
30840}
30841
30842fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.MemoizedStateStage) SemaError!void {
30843 const pt = sema.pt;
30844
30845 const unit: AnalUnit = .wrap(.{ .memoized_state = stage });
30846 try sema.addReferenceEntry(null, src, unit);
30847 try sema.declareDependency(.{ .memoized_state = stage });
30848
30849 const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined };
30850 if (pt.zcu.analysis_in_progress.contains(unit)) {
30851 return sema.failWithDependencyLoop(unit, &reason);
30852 }
30853 pt.ensureMemoizedStateUpToDate(stage, &reason) catch |err| switch (err) {
30854 error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = unit }),
30855 else => |e| return e,
30856 };
30857}
30858
30859pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void {
30860 const pt = sema.pt;
30861 const zcu = pt.zcu;
30862 const ip = &zcu.intern_pool;
30863
30864 const nav = ip.getNav(nav_index);
30865 if (nav.analysis == null) {
30866 assert(nav.resolved.?.value != .none);
30867 return;
30868 }
30869
30870 // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate`
30871 // to make sure the value is up-to-date on incremental updates.
30872
30873 const anal_unit: AnalUnit = .wrap(switch (kind) {
30874 .type => .{ .nav_ty = nav_index },
30875 .fully => .{ .nav_val = nav_index },
30876 });
30877 try sema.addReferenceEntry(block, src, anal_unit);
30878 try sema.declareDependency(switch (kind) {
30879 .type => .{ .nav_ty = nav_index },
30880 .fully => .{ .nav_val = nav_index },
30881 });
30882
30883 const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined };
30884
30885 if (zcu.analysis_in_progress.contains(anal_unit)) {
30886 return sema.failWithDependencyLoop(anal_unit, &reason);
30887 }
30888
30889 switch (kind) {
30890 .type => {
30891 try zcu.ensureNavValAnalysisQueued(nav_index);
30892 return pt.ensureNavTypeUpToDate(nav_index, &reason) catch |err| switch (err) {
30893 error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = anal_unit }),
30894 else => |e| return e,
30895 };
30896 },
30897 .fully => return pt.ensureNavValUpToDate(nav_index, &reason) catch |err| switch (err) {
30898 error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = anal_unit }),
30899 else => |e| return e,
30900 },
30901 }
30902}
30903
30904fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
30905 const pt = sema.pt;
30906 const ptr_anyopaque_ty = try pt.singleConstPtrType(.anyopaque);
30907 const opt_ptr_anyopaque_ty = try pt.optionalType(ptr_anyopaque_ty.toIntern());
30908 return .fromInterned(try pt.intern(.{ .opt = .{
30909 .ty = opt_ptr_anyopaque_ty.toIntern(),
30910 .val = payload: {
30911 const val = opt_val orelse break :payload .none;
30912 const ptr_val = try pt.getCoerced(try pt.uavValue(val), ptr_anyopaque_ty);
30913 break :payload ptr_val.toIntern();
30914 },
30915 } }));
30916}
30917
30918fn analyzeNavRef(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!Air.Inst.Ref {
30919 return sema.analyzeNavRefInner(block, src, nav_index, true);
30920}
30921
30922/// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed.
30923/// If this pointer will be used directly, `is_ref` must be `true`.
30924/// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`.
30925fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref {
30926 const pt = sema.pt;
30927 const zcu = pt.zcu;
30928 const ip = &zcu.intern_pool;
30929
30930 try sema.ensureNavResolved(block, src, orig_nav_index, if (is_ref) .type else .fully);
30931
30932 const nav_index = nav: {
30933 const orig_nav = ip.getNav(orig_nav_index);
30934 if (orig_nav.resolved.?.is_extern_decl or ip.zigTypeTag(orig_nav.resolved.?.type) == .@"fn") {
30935 // A pointer to this `Nav` might actually be encoded as a pointer to a different `Nav`
30936 // because this is either an `extern` definition or an `extern` alias. (The latter case
30937 // is unsolved language weirdness; see https://github.com/ziglang/zig/issues/21027.) To
30938 // know for sure how to encode this pointer, we need to check the *value* of this `Nav`.
30939 const orig_nav_value = switch (is_ref) {
30940 false => orig_nav.resolved.?.value,
30941 true => orig_val: {
30942 try sema.ensureNavResolved(block, src, orig_nav_index, .fully);
30943 break :orig_val ip.getNav(orig_nav_index).resolved.?.value;
30944 },
30945 };
30946 switch (ip.indexToKey(orig_nav_value)) {
30947 .func => |f| break :nav f.owner_nav,
30948 .@"extern" => |e| break :nav e.owner_nav,
30949 else => {},
30950 }
30951 }
30952 break :nav orig_nav_index;
30953 };
30954
30955 const nav_resolved = ip.getNav(nav_index).resolved.?;
30956
30957 const is_runtime: bool = runtime: {
30958 if (nav_resolved.@"threadlocal") break :runtime true;
30959 if (nav_resolved.value == .none) {
30960 // This didn't come from `@extern`, so even if extern it couldn't be dllimport or pcrel.
30961 break :runtime false;
30962 }
30963 const @"extern" = switch (ip.indexToKey(nav_resolved.value)) {
30964 .@"extern" => |e| e,
30965 else => break :runtime false,
30966 };
30967 if (@"extern".is_dll_import) break :runtime true;
30968 break :runtime switch (@"extern".relocation) {
30969 .any => false,
30970 .pcrel => true,
30971 };
30972 };
30973
30974 const ptr_ty = try pt.ptrType(.{
30975 .child = nav_resolved.type,
30976 .flags = .{
30977 .alignment = nav_resolved.@"align",
30978 .is_const = nav_resolved.@"const",
30979 .address_space = nav_resolved.@"addrspace",
30980 },
30981 });
30982
30983 if (is_runtime) {
30984 // This pointer is runtime-known; we need to emit an AIR instruction to create it.
30985 return block.addInst(.{
30986 .tag = .runtime_nav_ptr,
30987 .data = .{ .ty_nav = .{
30988 .ty = ptr_ty,
30989 .nav = nav_index,
30990 } },
30991 });
30992 }
30993
30994 if (is_ref) {
30995 try sema.maybeQueueFuncBodyAnalysis(block, src, nav_index);
30996 }
30997
30998 return Air.internedToRef((try pt.intern(.{ .ptr = .{
30999 .ty = ptr_ty.toIntern(),
31000 .base_addr = .{ .nav = nav_index },
31001 .byte_offset = 0,
31002 } })));
31003}
31004
31005fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void {
31006 const pt = sema.pt;
31007 const zcu = pt.zcu;
31008 const ip = &zcu.intern_pool;
31009
31010 // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function.
31011 // If it is, we can resolve the *value*, and queue analysis as needed.
31012
31013 try sema.ensureNavResolved(block, src, nav_index, .type);
31014 const nav_ty: Type = .fromInterned(ip.getNav(nav_index).resolved.?.type);
31015 if (nav_ty.zigTypeTag(zcu) != .@"fn") return;
31016 if (!nav_ty.fnHasRuntimeBits(zcu)) return;
31017
31018 try sema.ensureNavResolved(block, src, nav_index, .fully);
31019 const nav_val = zcu.navValue(nav_index);
31020 if (!ip.isFuncBody(nav_val.toIntern())) return;
31021
31022 const orig_fn_index = ip.unwrapCoercedFunc(nav_val.toIntern());
31023 try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index }));
31024 try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index);
31025}
31026
31027fn analyzeRef(
31028 sema: *Sema,
31029 block: *Block,
31030 src: LazySrcLoc,
31031 operand: Air.Inst.Ref,
31032 alignment: Alignment,
31033) CompileError!Air.Inst.Ref {
31034 const pt = sema.pt;
31035 const zcu = pt.zcu;
31036 const operand_ty = sema.typeOf(operand);
31037
31038 const address_space = target_util.defaultAddressSpace(zcu.getTarget(), .local);
31039 const ptr_type = try pt.ptrType(.{
31040 .child = operand_ty.toIntern(),
31041 .flags = .{
31042 .alignment = alignment,
31043 .is_const = true,
31044 .address_space = address_space,
31045 },
31046 });
31047
31048 if (sema.resolveValue(operand)) |val| {
31049 switch (zcu.intern_pool.indexToKey(val.toIntern())) {
31050 .@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav),
31051 .func => |f| return sema.analyzeNavRef(block, src, f.owner_nav),
31052 else => return .fromIntern(try pt.intern(.{ .ptr = .{
31053 .ty = ptr_type.toIntern(),
31054 .base_addr = .{ .uav = .{
31055 .val = val.toIntern(),
31056 .orig_ty = ptr_type.toIntern(),
31057 } },
31058 .byte_offset = 0,
31059 } })),
31060 }
31061 }
31062
31063 // No `requireRuntimeBlock`; it's okay to `ref` to a runtime value in a comptime context,
31064 // it's just that we can only use the *type* of the result, since the value is runtime-known.
31065
31066 const mut_ptr_type = try pt.ptrType(.{
31067 .child = operand_ty.toIntern(),
31068 .flags = .{
31069 .alignment = alignment,
31070 .is_const = false,
31071 .address_space = address_space,
31072 },
31073 });
31074 const alloc = try block.addTy(.alloc, mut_ptr_type);
31075
31076 // In a comptime context, the store would fail, since the operand is runtime-known. But that's
31077 // okay; we don't actually need this store to succeed, since we're creating a runtime value in a
31078 // comptime scope, so the value can never be used aside from to get its type.
31079 if (!block.isComptime()) {
31080 try sema.storePtr(block, src, alloc, operand);
31081 }
31082
31083 // Cast to the constant pointer type. We do this directly rather than going via `coerce` to
31084 // avoid errors in the `block.isComptime()` case.
31085 return block.addTyOp(.ptr_cast, ptr_type, alloc);
31086}
31087
31088fn analyzeLoad(
31089 sema: *Sema,
31090 block: *Block,
31091 src: LazySrcLoc,
31092 ptr: Air.Inst.Ref,
31093 ptr_src: LazySrcLoc,
31094) CompileError!Air.Inst.Ref {
31095 const pt = sema.pt;
31096 const zcu = pt.zcu;
31097 const ptr_ty = sema.typeOf(ptr);
31098 const elem_ty = switch (ptr_ty.zigTypeTag(zcu)) {
31099 .pointer => ptr_ty.childType(zcu),
31100 else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}),
31101 };
31102
31103 try sema.ensureLayoutResolved(elem_ty, src, .ptr_access);
31104
31105 if (elem_ty.isSpirvRuntimeArray(zcu)) {
31106 return sema.fail(block, src, "cannot load SPIR-V runtime array value", .{});
31107 }
31108
31109 const comptime_only = switch (elem_ty.classify(zcu)) {
31110 .no_possible_value => switch (elem_ty.zigTypeTag(zcu)) {
31111 .@"opaque" => return sema.fail(block, src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)}),
31112 else => {
31113 // Loading an uninstantiable type always invokes Illegal Behavior.
31114 if (block.isComptime()) {
31115 return sema.fail(block, src, "cannot load uninstantiable type '{f}'", .{elem_ty.fmt(pt)});
31116 } else if (block.wantSafety()) {
31117 try sema.safetyPanic(block, src, .load_uninstantiable_type);
31118 return .unreachable_value;
31119 } else {
31120 _ = try block.addNoOp(.unreach);
31121 return .unreachable_value;
31122 }
31123 },
31124 },
31125 .one_possible_value => return .fromValue((try elem_ty.onePossibleValue(pt)).?),
31126 .runtime => false,
31127 .partially_comptime, .fully_comptime => true,
31128 };
31129
31130 if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| {
31131 if (switch (ptr_ty.ptrSize(zcu)) {
31132 .slice => try sema.maybeDerefSliceAsArray(block, src, ptr_val),
31133 else => try sema.pointerDeref(block, src, ptr_val, ptr_ty),
31134 }) |elem_val| {
31135 return .fromValue(elem_val);
31136 }
31137 }
31138
31139 if (comptime_only) return sema.failWithOwnedErrorMsg(block, msg: {
31140 const msg = try sema.errMsg(src, "cannot load comptime-only type '{f}'", .{elem_ty.fmt(pt)});
31141 errdefer msg.destroy(zcu.gpa);
31142 try sema.errNote(ptr_src, msg, "pointer of type '{f}' is runtime-known", .{ptr_ty.fmt(pt)});
31143 break :msg msg;
31144 });
31145
31146 // https://github.com/ziglang/zig/issues/6597
31147 if (block.wantSafety() and ptr_ty.isCPtr(zcu)) {
31148 const is_non_null = try block.addUnOp(.is_non_null, ptr);
31149 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
31150 }
31151 return block.addTyOp(.load, elem_ty, ptr);
31152}
31153
31154fn analyzeSlicePtr(
31155 sema: *Sema,
31156 block: *Block,
31157 slice_src: LazySrcLoc,
31158 slice: Air.Inst.Ref,
31159 slice_ty: Type,
31160) CompileError!Air.Inst.Ref {
31161 const pt = sema.pt;
31162 const zcu = pt.zcu;
31163 const result_ty = slice_ty.slicePtrFieldType(zcu);
31164 if (sema.resolveValue(slice)) |val| {
31165 if (val.isUndef(zcu)) return pt.undefRef(result_ty);
31166 return Air.internedToRef(val.slicePtr(zcu).toIntern());
31167 }
31168 try sema.requireRuntimeBlock(block, slice_src, null);
31169 return block.addTyOp(.slice_ptr, result_ty, slice);
31170}
31171
31172fn analyzeOptionalSlicePtr(
31173 sema: *Sema,
31174 block: *Block,
31175 opt_slice_src: LazySrcLoc,
31176 opt_slice: Air.Inst.Ref,
31177 opt_slice_ty: Type,
31178) CompileError!Air.Inst.Ref {
31179 const pt = sema.pt;
31180 const zcu = pt.zcu;
31181 const slice_ty = opt_slice_ty.optionalChild(zcu);
31182 const result_ty = slice_ty.slicePtrFieldType(zcu);
31183
31184 if (sema.resolveValue(opt_slice)) |opt_val| {
31185 if (opt_val.isUndef(zcu)) return pt.undefRef(result_ty);
31186 const slice_ptr: InternPool.Index = if (opt_val.optionalValue(zcu)) |val|
31187 val.slicePtr(zcu).toIntern()
31188 else
31189 .null_value;
31190
31191 return Air.internedToRef(slice_ptr);
31192 }
31193
31194 try sema.requireRuntimeBlock(block, opt_slice_src, null);
31195
31196 const slice = try block.addTyOp(.optional_payload, slice_ty, opt_slice);
31197 return block.addTyOp(.slice_ptr, result_ty, slice);
31198}
31199
31200fn analyzeSliceLen(
31201 sema: *Sema,
31202 block: *Block,
31203 src: LazySrcLoc,
31204 slice_inst: Air.Inst.Ref,
31205) CompileError!Air.Inst.Ref {
31206 const pt = sema.pt;
31207 const zcu = pt.zcu;
31208 if (sema.resolveValue(slice_inst)) |slice_val| {
31209 if (slice_val.isUndef(zcu)) {
31210 return .undef_usize;
31211 }
31212 return pt.intRef(.usize, slice_val.sliceLen(zcu));
31213 }
31214 try sema.requireRuntimeBlock(block, src, null);
31215 return block.addTyOp(.slice_len, .usize, slice_inst);
31216}
31217
31218fn analyzeIsNull(
31219 sema: *Sema,
31220 block: *Block,
31221 src: LazySrcLoc,
31222 operand: Air.Inst.Ref,
31223 invert_logic: bool,
31224) CompileError!Air.Inst.Ref {
31225 const pt = sema.pt;
31226 const zcu = pt.zcu;
31227
31228 if (try sema.resolveIsNullFromType(block, src, sema.typeOf(operand))) |is_null| {
31229 return .fromValue(.makeBool(is_null != invert_logic)); // XOR
31230 }
31231
31232 if (sema.resolveValue(operand)) |opt_val| {
31233 if (opt_val.isUndef(zcu)) {
31234 return pt.undefRef(.bool);
31235 }
31236 const is_null = opt_val.isNull(zcu);
31237 return .fromValue(.makeBool(is_null != invert_logic)); // XOR
31238 }
31239
31240 const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null;
31241 return block.addUnOp(air_tag, operand);
31242}
31243
31244fn resolvePtrIsNonErrVal(
31245 sema: *Sema,
31246 block: *Block,
31247 src: LazySrcLoc,
31248 operand: Air.Inst.Ref,
31249) CompileError!?Value {
31250 const pt = sema.pt;
31251 const zcu = pt.zcu;
31252 const ptr_ty = sema.typeOf(operand);
31253 assert(ptr_ty.zigTypeTag(zcu) == .pointer);
31254 const child_ty = ptr_ty.childType(zcu);
31255
31256 if (try sema.resolveIsNonErrFromType(block, src, child_ty)) |res| {
31257 return res;
31258 }
31259 assert(child_ty.zigTypeTag(zcu) == .error_union);
31260
31261 if (sema.resolveValue(operand)) |eu_ptr_val| {
31262 if (eu_ptr_val.isUndef(zcu)) return .undef_bool;
31263 if (try sema.pointerDeref(block, src, eu_ptr_val, ptr_ty)) |err_union| {
31264 if (err_union.isUndef(zcu)) return .undef_bool;
31265 return .makeBool(err_union.getErrorName(zcu) == .none);
31266 }
31267 }
31268
31269 return null;
31270}
31271
31272fn resolveIsNonErrVal(
31273 sema: *Sema,
31274 block: *Block,
31275 src: LazySrcLoc,
31276 operand: Air.Inst.Ref,
31277) CompileError!?Value {
31278 const zcu = sema.pt.zcu;
31279 if (try sema.resolveIsNonErrFromType(block, src, sema.typeOf(operand))) |res| {
31280 return res;
31281 }
31282 assert(sema.typeOf(operand).zigTypeTag(zcu) == .error_union);
31283
31284 if (sema.resolveValue(operand)) |err_union| {
31285 if (err_union.isUndef(zcu)) return .undef_bool;
31286 return .makeBool(err_union.getErrorName(zcu) == .none);
31287 }
31288
31289 return null;
31290}
31291
31292/// If `null` is the only possible value of type `ty`, returns `true`.
31293/// If `null` is *not* a possible value of `ty`, returns `false`.
31294/// Otherwise, if a value of type `ty` may or may not be `null`, returns `null`.
31295///
31296/// Asserts that the layout of `ty` is resolved.
31297fn resolveIsNullFromType(
31298 sema: *Sema,
31299 block: *Block,
31300 src: LazySrcLoc,
31301 ty: Type,
31302) CompileError!?bool {
31303 const zcu = sema.pt.zcu;
31304 ty.assertHasLayout(zcu);
31305
31306 return switch (ty.zigTypeTag(zcu)) {
31307 else => false,
31308 .null => true,
31309 .pointer => switch (ty.ptrSize(zcu)) {
31310 .c => null,
31311 else => false,
31312 },
31313 .optional => {
31314 const payload_ty = ty.optionalChild(zcu);
31315 if (payload_ty.classify(zcu) == .no_possible_value) {
31316 return true; // e.g. `?noreturn`
31317 }
31318 if (payload_ty.zigTypeTag(zcu) == .error_set and
31319 try sema.resolveErrSetIsEmpty(block, src, payload_ty))
31320 {
31321 return true; // e.g. `?error{}`
31322 }
31323 return null;
31324 },
31325 };
31326}
31327
31328fn resolveIsNonErrFromType(
31329 sema: *Sema,
31330 block: *Block,
31331 src: LazySrcLoc,
31332 operand_ty: Type,
31333) CompileError!?Value {
31334 const pt = sema.pt;
31335 const zcu = pt.zcu;
31336 const ot = operand_ty.zigTypeTag(zcu);
31337 if (ot != .error_set and ot != .error_union) return .true;
31338 if (ot == .error_set) return .false;
31339 assert(ot == .error_union);
31340
31341 const payload_ty = operand_ty.errorUnionPayload(zcu);
31342 if (payload_ty.classify(zcu) == .no_possible_value) {
31343 return .false;
31344 }
31345 if (try sema.resolveErrSetIsEmpty(block, src, operand_ty.errorUnionSet(zcu))) {
31346 return .true;
31347 }
31348 return null;
31349}
31350
31351/// Returns `true` iff the error set type `orig_err_set_ty` contains no errors.
31352///
31353/// This is used to give comptime answers for whether `error{}!T` is an error or a payload, as well
31354/// as whether `?error{}` is null. The type `error{}` cannot be NPV, as it has runtime bits, but the
31355/// only value of that type which can exist is `undefined`; semantically it has no "legal" value.
31356/// TODO: this runs into some unsolved language design questions about such types. Performing a
31357/// coercion from `@as(E, undefined)` to `E!T` needs to semantically result in an `undefined` error
31358/// union if our implementation is to be legal, and likewise for coercing `@as(E, undefined)` to
31359/// `?E` (for an error set `E`) because our implementation uses the zero error value at runtime to
31360/// represent `null`. The unsolved problem is the exact rules for `undefined` propagation through
31361/// these types: for instance, what if `@as(u32, undfined)` is coerced to `?u32`? What about error
31362/// union *payloads*, i.e. `@as(u32, undefined)` to `E!u32`? That one is analagous to the optional
31363/// example in some ways, but right now I believe there is code which relies on that coercion giving
31364/// a well-defined error union with an `undefined` payload.
31365/// Relevant issues/discussions:
31366/// * https://github.com/ziglang/zig/issues/1831
31367/// * https://github.com/ziglang/zig/issues/6762
31368/// * https://github.com/ziglang/zig/issues/1831#issuecomment-722129239
31369fn resolveErrSetIsEmpty(
31370 sema: *Sema,
31371 block: *Block,
31372 src: LazySrcLoc,
31373 orig_err_set_ty: Type,
31374) CompileError!bool {
31375 const ip = &sema.pt.zcu.intern_pool;
31376 err_set: switch (orig_err_set_ty.toIntern()) {
31377 .anyerror_type => return false,
31378 .adhoc_inferred_error_set_type => {
31379 // This is *our* error set; that is, we're currently analyzing the function
31380 // which owns it. Trying to resolve it now would cause a dependency loop.
31381 // Instead, accept that we don't know.
31382 return false;
31383 },
31384 else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) {
31385 .error_set_type => |es| return es.names.len == 0,
31386 .inferred_error_set_type => |func_index| {
31387 if (sema.fn_ret_ty_ies) |ies| {
31388 if (ies.func == func_index) {
31389 // This is *our* error set; that is, we're currently analyzing the function
31390 // which owns it. Trying to resolve it now would cause a dependency loop.
31391 // Instead, accept that we don't know.
31392 return false;
31393 }
31394 }
31395 try sema.ensureFuncIesResolved(block, src, func_index);
31396 continue :err_set ip.funcIesResolvedUnordered(func_index);
31397 },
31398 else => unreachable,
31399 },
31400 }
31401}
31402
31403fn analyzeIsNonErr(
31404 sema: *Sema,
31405 block: *Block,
31406 src: LazySrcLoc,
31407 operand: Air.Inst.Ref,
31408) CompileError!Air.Inst.Ref {
31409 if (try sema.resolveIsNonErrVal(block, src, operand)) |val| {
31410 return .fromValue(val);
31411 } else {
31412 return block.addUnOp(.is_non_err, operand);
31413 }
31414}
31415
31416fn analyzePtrIsNonErr(
31417 sema: *Sema,
31418 block: *Block,
31419 src: LazySrcLoc,
31420 operand: Air.Inst.Ref,
31421) CompileError!Air.Inst.Ref {
31422 if (try sema.resolvePtrIsNonErrVal(block, src, operand)) |val| {
31423 return .fromValue(val);
31424 } else {
31425 return block.addUnOp(.is_non_err_ptr, operand);
31426 }
31427}
31428
31429fn analyzeSlice(
31430 sema: *Sema,
31431 block: *Block,
31432 src: LazySrcLoc,
31433 ptr_ptr: Air.Inst.Ref,
31434 uncasted_start: Air.Inst.Ref,
31435 uncasted_end_opt: Air.Inst.Ref,
31436 sentinel_opt: Air.Inst.Ref,
31437 sentinel_src: LazySrcLoc,
31438 ptr_src: LazySrcLoc,
31439 start_src: LazySrcLoc,
31440 end_src: LazySrcLoc,
31441 by_length: bool,
31442) CompileError!Air.Inst.Ref {
31443 const pt = sema.pt;
31444 const zcu = pt.zcu;
31445 // Slice expressions can operate on a variable whose type is an array. This requires
31446 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
31447 const ptr_ptr_ty = sema.typeOf(ptr_ptr);
31448 const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(zcu)) {
31449 .pointer => ptr_ptr_ty.childType(zcu),
31450 else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ptr_ty.fmt(pt)}),
31451 };
31452
31453 var array_ty = ptr_ptr_child_ty;
31454 var slice_ty = ptr_ptr_ty;
31455 var ptr_or_slice = ptr_ptr;
31456 var elem_ty: Type = undefined;
31457 var ptr_sentinel: ?Value = null;
31458 switch (ptr_ptr_child_ty.zigTypeTag(zcu)) {
31459 .array => {
31460 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu);
31461 elem_ty = ptr_ptr_child_ty.childType(zcu);
31462 },
31463 .pointer => switch (ptr_ptr_child_ty.ptrSize(zcu)) {
31464 .one => {
31465 const double_child_ty = ptr_ptr_child_ty.childType(zcu);
31466 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
31467 if (double_child_ty.zigTypeTag(zcu) == .array) {
31468 ptr_sentinel = double_child_ty.sentinel(zcu);
31469 slice_ty = ptr_ptr_child_ty;
31470 array_ty = double_child_ty;
31471 elem_ty = double_child_ty.childType(zcu);
31472 } else {
31473 if (uncasted_end_opt == .none) {
31474 return sema.fail(block, src, "slice of single-item pointer must be bounded", .{});
31475 }
31476 const start_value = try sema.resolveConstDefinedValue(
31477 block,
31478 start_src,
31479 uncasted_start,
31480 .{ .simple = .slice_single_item_ptr_bounds },
31481 );
31482
31483 const end_value = try sema.resolveConstDefinedValue(
31484 block,
31485 end_src,
31486 uncasted_end_opt,
31487 .{ .simple = .slice_single_item_ptr_bounds },
31488 );
31489
31490 const bounds_error_message = "slice of single-item pointer must have bounds [0..0], [0..1], or [1..1]";
31491 if (try sema.compareScalar(start_value, .neq, end_value, .comptime_int)) {
31492 if (try sema.compareScalar(start_value, .neq, Value.zero_comptime_int, .comptime_int)) {
31493 const msg = msg: {
31494 const msg = try sema.errMsg(start_src, bounds_error_message, .{});
31495 errdefer msg.destroy(sema.gpa);
31496 try sema.errNote(
31497 start_src,
31498 msg,
31499 "expected '{f}', found '{f}'",
31500 .{
31501 Value.zero_comptime_int.fmtValueSema(pt, sema),
31502 start_value.fmtValueSema(pt, sema),
31503 },
31504 );
31505 break :msg msg;
31506 };
31507 return sema.failWithOwnedErrorMsg(block, msg);
31508 } else if (try sema.compareScalar(end_value, .neq, Value.one_comptime_int, .comptime_int)) {
31509 const msg = msg: {
31510 const msg = try sema.errMsg(end_src, bounds_error_message, .{});
31511 errdefer msg.destroy(sema.gpa);
31512 try sema.errNote(
31513 end_src,
31514 msg,
31515 "expected '{f}', found '{f}'",
31516 .{
31517 Value.one_comptime_int.fmtValueSema(pt, sema),
31518 end_value.fmtValueSema(pt, sema),
31519 },
31520 );
31521 break :msg msg;
31522 };
31523 return sema.failWithOwnedErrorMsg(block, msg);
31524 }
31525 } else {
31526 if (try sema.compareScalar(end_value, .gt, Value.one_comptime_int, .comptime_int)) {
31527 return sema.fail(
31528 block,
31529 end_src,
31530 "end index {f} out of bounds for slice of single-item pointer",
31531 .{end_value.fmtValueSema(pt, sema)},
31532 );
31533 }
31534 }
31535
31536 array_ty = try pt.arrayType(.{
31537 .len = 1,
31538 .child = double_child_ty.toIntern(),
31539 });
31540 const ptr_info = ptr_ptr_child_ty.ptrInfo(zcu);
31541 slice_ty = try pt.ptrType(.{
31542 .child = array_ty.toIntern(),
31543 .flags = .{
31544 .alignment = ptr_info.flags.alignment,
31545 .is_const = ptr_info.flags.is_const,
31546 .is_allowzero = ptr_info.flags.is_allowzero,
31547 .is_volatile = ptr_info.flags.is_volatile,
31548 .address_space = ptr_info.flags.address_space,
31549 },
31550 });
31551 elem_ty = double_child_ty;
31552 }
31553 },
31554 .many, .c => {
31555 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu);
31556 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
31557 slice_ty = ptr_ptr_child_ty;
31558 array_ty = ptr_ptr_child_ty;
31559 elem_ty = ptr_ptr_child_ty.childType(zcu);
31560
31561 if (ptr_ptr_child_ty.ptrSize(zcu) == .c) {
31562 if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| {
31563 if (ptr_val.isNull(zcu)) {
31564 return sema.fail(block, src, "slice of null pointer", .{});
31565 }
31566 }
31567 }
31568 },
31569 .slice => {
31570 ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu);
31571 ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
31572 slice_ty = ptr_ptr_child_ty;
31573 array_ty = ptr_ptr_child_ty;
31574 elem_ty = ptr_ptr_child_ty.childType(zcu);
31575 },
31576 },
31577 else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}),
31578 }
31579
31580 try sema.ensureLayoutResolved(elem_ty, src, .ptr_access);
31581 try sema.checkSpirvSliceAllowed(block, src, slice_ty.ptrInfo(zcu).flags.address_space);
31582
31583 const ptr = if (slice_ty.isSlice(zcu))
31584 try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty)
31585 else if (array_ty.zigTypeTag(zcu) == .array) ptr: {
31586 var manyptr_ty_key = zcu.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type;
31587 assert(manyptr_ty_key.child == array_ty.toIntern());
31588 assert(manyptr_ty_key.flags.size == .one);
31589 manyptr_ty_key.child = elem_ty.toIntern();
31590 manyptr_ty_key.flags.size = .many;
31591 break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src);
31592 } else ptr_or_slice;
31593
31594 const start = try sema.coerce(block, .usize, uncasted_start, start_src);
31595 const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src);
31596 const new_ptr_ty = sema.typeOf(new_ptr);
31597
31598 // true if and only if the end index of the slice, implicitly or explicitly, equals
31599 // the length of the underlying object being sliced. we might learn the length of the
31600 // underlying object because it is an array (which has the length in the type), or
31601 // we might learn of the length because it is a comptime-known slice value.
31602 var end_is_len = uncasted_end_opt == .none;
31603 const end = e: {
31604 if (array_ty.zigTypeTag(zcu) == .array) {
31605 const len_val = try pt.intValue(.usize, array_ty.arrayLen(zcu));
31606
31607 if (!end_is_len) {
31608 const end = if (by_length) end: {
31609 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src);
31610 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
31611 break :end try sema.coerce(block, .usize, uncasted_end, end_src);
31612 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src);
31613 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
31614 const len_s_val = try pt.intValue(
31615 .usize,
31616 array_ty.arrayLenIncludingSentinel(zcu),
31617 );
31618 if (!(try sema.compareAll(end_val, .lte, len_s_val, .usize))) {
31619 const sentinel_label: []const u8 = if (array_ty.sentinel(zcu) != null)
31620 " +1 (sentinel)"
31621 else
31622 "";
31623
31624 return sema.fail(
31625 block,
31626 end_src,
31627 "end index {f} out of bounds for array of length {f}{s}",
31628 .{
31629 end_val.fmtValueSema(pt, sema),
31630 len_val.fmtValueSema(pt, sema),
31631 sentinel_label,
31632 },
31633 );
31634 }
31635
31636 // end_is_len is only true if we are NOT using the sentinel
31637 // length. For sentinel-length, we don't want the type to
31638 // contain the sentinel.
31639 if (end_val.eql(len_val, .usize, zcu)) {
31640 end_is_len = true;
31641 }
31642 }
31643 break :e end;
31644 }
31645
31646 break :e Air.internedToRef(len_val.toIntern());
31647 } else if (slice_ty.isSlice(zcu)) {
31648 if (!end_is_len) {
31649 const end = if (by_length) end: {
31650 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src);
31651 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
31652 break :end try sema.coerce(block, .usize, uncasted_end, end_src);
31653 } else try sema.coerce(block, .usize, uncasted_end_opt, end_src);
31654 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
31655 if (sema.resolveValue(ptr_or_slice)) |slice_val| {
31656 if (slice_val.isUndef(zcu)) {
31657 return sema.fail(block, src, "slice of undefined", .{});
31658 }
31659 const has_sentinel = slice_ty.sentinel(zcu) != null;
31660 const slice_len = slice_val.sliceLen(zcu);
31661 const len_plus_sent = slice_len + @intFromBool(has_sentinel);
31662 const slice_len_val_with_sentinel = try pt.intValue(.usize, len_plus_sent);
31663 if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, .usize))) {
31664 const sentinel_label: []const u8 = if (has_sentinel)
31665 " +1 (sentinel)"
31666 else
31667 "";
31668
31669 return sema.fail(
31670 block,
31671 end_src,
31672 "end index {f} out of bounds for slice of length {d}{s}",
31673 .{
31674 end_val.fmtValueSema(pt, sema),
31675 slice_val.sliceLen(zcu),
31676 sentinel_label,
31677 },
31678 );
31679 }
31680
31681 // If the slice has a sentinel, we consider end_is_len
31682 // is only true if it equals the length WITHOUT the
31683 // sentinel, so we don't add a sentinel type.
31684 const slice_len_val = try pt.intValue(.usize, slice_len);
31685 if (end_val.eql(slice_len_val, .usize, zcu)) {
31686 end_is_len = true;
31687 }
31688 }
31689 }
31690 break :e end;
31691 }
31692 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
31693 }
31694 if (!end_is_len) {
31695 if (by_length) {
31696 const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src);
31697 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
31698 break :e try sema.coerce(block, .usize, uncasted_end, end_src);
31699 } else break :e try sema.coerce(block, .usize, uncasted_end_opt, end_src);
31700 }
31701
31702 // when slicing a many-item pointer, if a sentinel `S` is provided as in `ptr[a.. :S]`, it
31703 // must match the sentinel of `@TypeOf(ptr)`.
31704 sentinel_check: {
31705 if (sentinel_opt == .none) break :sentinel_check;
31706 const provided = provided: {
31707 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
31708 try checkSentinelType(sema, block, sentinel_src, elem_ty);
31709 break :provided try sema.resolveConstDefinedValue(
31710 block,
31711 sentinel_src,
31712 casted,
31713 .{ .simple = .slice_sentinel },
31714 );
31715 };
31716
31717 if (ptr_sentinel) |current| {
31718 if (provided.toIntern() == current.toIntern()) break :sentinel_check;
31719 }
31720
31721 return sema.failWithOwnedErrorMsg(block, msg: {
31722 const msg = try sema.errMsg(sentinel_src, "sentinel-terminated slicing of many-item pointer must match existing sentinel", .{});
31723 errdefer msg.destroy(sema.gpa);
31724 if (ptr_sentinel) |current| {
31725 try sema.errNote(sentinel_src, msg, "expected sentinel '{f}', found '{f}'", .{ current.fmtValue(pt), provided.fmtValue(pt) });
31726 } else {
31727 try sema.errNote(ptr_src, msg, "type '{f}' does not have a sentinel", .{slice_ty.fmt(pt)});
31728 }
31729 try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{});
31730 break :msg msg;
31731 });
31732 }
31733 return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src);
31734 };
31735
31736 const sentinel = s: {
31737 if (sentinel_opt != .none) {
31738 const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src);
31739 try checkSentinelType(sema, block, sentinel_src, elem_ty);
31740 break :s try sema.resolveConstDefinedValue(block, sentinel_src, casted, .{ .simple = .slice_sentinel });
31741 }
31742 // If we are slicing to the end of something that is sentinel-terminated
31743 // then the resulting slice type is also sentinel-terminated.
31744 if (end_is_len) {
31745 if (ptr_sentinel) |sent| {
31746 break :s sent;
31747 }
31748 }
31749 break :s null;
31750 };
31751 const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
31752
31753 var checked_start_lte_end = by_length;
31754 var runtime_src: ?LazySrcLoc = null;
31755
31756 // requirement: start <= end
31757 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
31758 if (try sema.compareAll(start_val, .eq, .zero_usize, .usize)) {
31759 checked_start_lte_end = true;
31760 }
31761 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
31762 if (!checked_start_lte_end and
31763 !by_length and
31764 !(try sema.compareAll(start_val, .lte, end_val, .usize)))
31765 {
31766 return sema.fail(
31767 block,
31768 start_src,
31769 "start index {f} is larger than end index {f}",
31770 .{
31771 start_val.fmtValueSema(pt, sema),
31772 end_val.fmtValueSema(pt, sema),
31773 },
31774 );
31775 }
31776 checked_start_lte_end = true;
31777 if (sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: {
31778 const expected_sentinel = sentinel orelse break :sentinel_check;
31779 const start_int = start_val.toUnsignedInt(zcu);
31780 const end_int = end_val.toUnsignedInt(zcu);
31781 const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
31782
31783 const many_ptr_ty = try pt.manyConstPtrType(elem_ty);
31784 const many_ptr_val = try pt.getCoerced(ptr_val, many_ptr_ty);
31785 const elem_ptr = try many_ptr_val.ptrElem(sentinel_index, pt);
31786 const res = try sema.pointerDerefExtra(block, src, elem_ptr);
31787 const actual_sentinel = switch (res) {
31788 .runtime_load => break :sentinel_check,
31789 .val => |v| v,
31790 .needed_well_defined => |ty| return sema.fail(
31791 block,
31792 src,
31793 "comptime dereference requires '{f}' to have a well-defined layout",
31794 .{ty.fmt(pt)},
31795 ),
31796 .out_of_bounds => |ty| return sema.fail(
31797 block,
31798 end_src,
31799 "slice end index {d} exceeds bounds of containing decl of type '{f}'",
31800 .{ end_int, ty.fmt(pt) },
31801 ),
31802 };
31803
31804 if (!actual_sentinel.eql(expected_sentinel, elem_ty, zcu)) {
31805 const msg = msg: {
31806 const msg = try sema.errMsg(src, "value in memory does not match slice sentinel", .{});
31807 errdefer msg.destroy(sema.gpa);
31808 try sema.errNote(src, msg, "expected '{f}', found '{f}'", .{
31809 expected_sentinel.fmtValueSema(pt, sema),
31810 actual_sentinel.fmtValueSema(pt, sema),
31811 });
31812
31813 break :msg msg;
31814 };
31815 return sema.failWithOwnedErrorMsg(block, msg);
31816 }
31817 } else {
31818 runtime_src = ptr_src;
31819 }
31820 } else {
31821 runtime_src = end_src;
31822 }
31823 } else {
31824 runtime_src = start_src;
31825 }
31826
31827 if (!checked_start_lte_end and block.wantSafety() and !block.isComptime()) {
31828 // requirement: start <= end
31829 assert(!block.isComptime());
31830 try sema.requireRuntimeBlock(block, src, runtime_src.?);
31831 const ok = try block.addBinOp(.cmp_lte, start, end);
31832 try sema.addSafetyCheckCall(block, src, ok, .@"panic.startGreaterThanEnd", &.{ start, end });
31833 }
31834 const new_len = if (by_length)
31835 try sema.coerce(block, .usize, uncasted_end_opt, end_src)
31836 else
31837 try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
31838 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
31839
31840 const new_ptr_ty_info = new_ptr_ty.ptrInfo(zcu);
31841 const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(zcu) != .c;
31842
31843 if (opt_new_len_val) |new_len_val| {
31844 const new_len_int = new_len_val.toUnsignedInt(zcu);
31845
31846 const return_ty = try pt.ptrType(.{
31847 .child = (try pt.arrayType(.{
31848 .len = new_len_int,
31849 .sentinel = if (sentinel) |s| s.toIntern() else .none,
31850 .child = elem_ty.toIntern(),
31851 })).toIntern(),
31852 .flags = .{
31853 .alignment = new_ptr_ty_info.flags.alignment,
31854 .is_const = new_ptr_ty_info.flags.is_const,
31855 .is_allowzero = new_allowzero,
31856 .is_volatile = new_ptr_ty_info.flags.is_volatile,
31857 .address_space = new_ptr_ty_info.flags.address_space,
31858 },
31859 });
31860
31861 const opt_new_ptr_val = sema.resolveValue(new_ptr);
31862 const new_ptr_val = opt_new_ptr_val orelse {
31863 const result = try block.addTyOp(.ptr_cast, return_ty, new_ptr);
31864 if (block.wantSafety()) {
31865 // requirement: slicing C ptr is non-null
31866 if (ptr_ptr_child_ty.isCPtr(zcu)) {
31867 const is_non_null = try block.addUnOp(.is_non_null, ptr);
31868 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
31869 }
31870
31871 bounds_check: {
31872 const actual_len = if (array_ty.zigTypeTag(zcu) == .array)
31873 try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu))
31874 else if (slice_ty.isSlice(zcu)) l: {
31875 const slice_len = try sema.analyzeSliceLen(block, src, ptr_or_slice);
31876 break :l if (slice_ty.sentinel(zcu) == null)
31877 slice_len
31878 else
31879 try sema.analyzeArithmetic(block, .add, slice_len, .one, src, end_src, end_src, true);
31880 } else break :bounds_check;
31881
31882 const actual_end = if (slice_sentinel != null)
31883 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
31884 else
31885 end;
31886
31887 if (try sema.resolveDefinedValue(block, src, actual_len) == null or
31888 try sema.resolveDefinedValue(block, src, actual_end) == null)
31889 try sema.addSafetyCheckIndexOob(block, src, actual_end, actual_len, .cmp_lte);
31890 }
31891
31892 // requirement: result[new_len] == slice_sentinel
31893 try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
31894 }
31895 return result;
31896 };
31897
31898 if (!new_ptr_val.isUndef(zcu)) {
31899 return Air.internedToRef((try pt.getCoerced(new_ptr_val, return_ty)).toIntern());
31900 }
31901
31902 // Special case: @as([]i32, undefined)[x..x]
31903 if (new_len_int == 0) {
31904 return pt.undefRef(return_ty);
31905 }
31906
31907 return sema.fail(block, src, "non-zero length slice of undefined pointer", .{});
31908 }
31909
31910 const return_ty = try pt.ptrType(.{
31911 .child = elem_ty.toIntern(),
31912 .sentinel = if (sentinel) |s| s.toIntern() else .none,
31913 .flags = .{
31914 .size = .slice,
31915 .alignment = new_ptr_ty_info.flags.alignment,
31916 .is_const = new_ptr_ty_info.flags.is_const,
31917 .is_volatile = new_ptr_ty_info.flags.is_volatile,
31918 .is_allowzero = new_allowzero,
31919 .address_space = new_ptr_ty_info.flags.address_space,
31920 },
31921 });
31922
31923 try sema.requireRuntimeBlock(block, src, runtime_src.?);
31924 if (block.wantSafety()) {
31925 // requirement: slicing C ptr is non-null
31926 if (ptr_ptr_child_ty.isCPtr(zcu)) {
31927 const is_non_null = try block.addUnOp(.is_non_null, ptr);
31928 try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null);
31929 }
31930
31931 // requirement: end <= len
31932 const opt_len_inst = if (array_ty.zigTypeTag(zcu) == .array)
31933 try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu))
31934 else if (slice_ty.isSlice(zcu)) blk: {
31935 if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
31936 // we don't need to add one for sentinels because the
31937 // underlying value data includes the sentinel
31938 break :blk try pt.intRef(.usize, slice_val.sliceLen(zcu));
31939 }
31940
31941 const slice_len_inst = try block.addTyOp(.slice_len, .usize, ptr_or_slice);
31942 if (slice_ty.sentinel(zcu) == null) break :blk slice_len_inst;
31943
31944 // we have to add one because slice lengths don't include the sentinel
31945 break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true);
31946 } else null;
31947 if (opt_len_inst) |len_inst| {
31948 const actual_end = if (slice_sentinel != null)
31949 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
31950 else
31951 end;
31952 try sema.addSafetyCheckIndexOob(block, src, actual_end, len_inst, .cmp_lte);
31953 }
31954 }
31955 const result = try block.addInst(.{
31956 .tag = .slice,
31957 .data = .{ .ty_pl = .{
31958 .ty = return_ty,
31959 .payload = try sema.addExtra(Air.Bin{
31960 .lhs = new_ptr,
31961 .rhs = new_len,
31962 }),
31963 } },
31964 });
31965 if (block.wantSafety()) {
31966 // requirement: result[new_len] == slice_sentinel
31967 try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
31968 }
31969 return result;
31970}
31971
31972/// Asserts that lhs and rhs types are both numeric.
31973fn cmpNumeric(
31974 sema: *Sema,
31975 block: *Block,
31976 src: LazySrcLoc,
31977 uncasted_lhs: Air.Inst.Ref,
31978 uncasted_rhs: Air.Inst.Ref,
31979 op: std.math.CompareOperator,
31980 lhs_src: LazySrcLoc,
31981 rhs_src: LazySrcLoc,
31982) CompileError!Air.Inst.Ref {
31983 const pt = sema.pt;
31984 const zcu = pt.zcu;
31985 const lhs_ty = sema.typeOf(uncasted_lhs);
31986 const rhs_ty = sema.typeOf(uncasted_rhs);
31987
31988 assert(lhs_ty.isNumeric(zcu));
31989 assert(rhs_ty.isNumeric(zcu));
31990
31991 const lhs_ty_tag = lhs_ty.zigTypeTag(zcu);
31992 const rhs_ty_tag = rhs_ty.zigTypeTag(zcu);
31993 const target = zcu.getTarget();
31994
31995 // One exception to heterogeneous comparison: comptime_float needs to
31996 // coerce to fixed-width float.
31997
31998 const lhs = if (lhs_ty_tag == .comptime_float and rhs_ty_tag == .float)
31999 try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src)
32000 else
32001 uncasted_lhs;
32002
32003 const rhs = if (lhs_ty_tag == .float and rhs_ty_tag == .comptime_float)
32004 try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src)
32005 else
32006 uncasted_rhs;
32007
32008 const maybe_lhs_val = sema.resolveValue(lhs);
32009 const maybe_rhs_val = sema.resolveValue(rhs);
32010
32011 // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS value.
32012 if (maybe_lhs_val) |lhs_val| {
32013 // Result based on comparison exceeding type bounds
32014 if (!lhs_val.isUndef(zcu) and (lhs_ty_tag == .int or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) {
32015 if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| {
32016 return if (res) .bool_true else .bool_false;
32017 }
32018 }
32019 // Result based on NaN comparison
32020 if (lhs_val.isNan(zcu)) {
32021 return if (op == .neq) .bool_true else .bool_false;
32022 }
32023 // Result based on inf comparison to int
32024 if (lhs_val.isInf(zcu) and rhs_ty_tag == .int) return switch (op) {
32025 .neq => .bool_true,
32026 .eq => .bool_false,
32027 .gt, .gte => if (lhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
32028 .lt, .lte => if (lhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
32029 };
32030 }
32031
32032 // If the RHS is const, check if there is a guaranteed result which does not depend on ths LHS value.
32033 if (maybe_rhs_val) |rhs_val| {
32034 // Result based on comparison exceeding type bounds
32035 if (!rhs_val.isUndef(zcu) and (rhs_ty_tag == .int or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) {
32036 if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| {
32037 return if (res) .bool_true else .bool_false;
32038 }
32039 }
32040 // Result based on NaN comparison
32041 if (rhs_val.isNan(zcu)) {
32042 return if (op == .neq) .bool_true else .bool_false;
32043 }
32044 // Result based on inf comparison to int
32045 if (rhs_val.isInf(zcu) and lhs_ty_tag == .int) return switch (op) {
32046 .neq => .bool_true,
32047 .eq => .bool_false,
32048 .gt, .gte => if (rhs_val.isNegativeInf(zcu)) .bool_true else .bool_false,
32049 .lt, .lte => if (rhs_val.isNegativeInf(zcu)) .bool_false else .bool_true,
32050 };
32051 }
32052
32053 // Any other comparison depends on both values, so the result is undef if either is undef.
32054 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
32055 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool;
32056
32057 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: {
32058 if (maybe_rhs_val) |rhs_val| {
32059 return .fromValue(.makeBool(Value.compareHetero(lhs_val, op, rhs_val, zcu)));
32060 } else break :rs rhs_src;
32061 } else lhs_src;
32062
32063 try sema.requireRuntimeBlock(block, src, runtime_src);
32064
32065 // For floats, emit a float comparison instruction.
32066 const lhs_is_float = switch (lhs_ty_tag) {
32067 .float, .comptime_float => true,
32068 else => false,
32069 };
32070 const rhs_is_float = switch (rhs_ty_tag) {
32071 .float, .comptime_float => true,
32072 else => false,
32073 };
32074
32075 if (lhs_is_float and rhs_is_float) {
32076 // Smaller fixed-width floats coerce to larger fixed-width floats.
32077 // comptime_float coerces to fixed-width float.
32078 const dest_ty = x: {
32079 if (lhs_ty_tag == .comptime_float) {
32080 break :x rhs_ty;
32081 } else if (rhs_ty_tag == .comptime_float) {
32082 break :x lhs_ty;
32083 }
32084 if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) {
32085 break :x lhs_ty;
32086 } else {
32087 break :x rhs_ty;
32088 }
32089 };
32090 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
32091 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
32092 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs);
32093 }
32094
32095 // For mixed unsigned integer sizes, implicit cast both operands to the larger integer.
32096 // For mixed signed and unsigned integers, implicit cast both operands to a signed
32097 // integer with + 1 bit.
32098 // For mixed floats and integers, extract the integer part from the float, cast that to
32099 // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float,
32100 // add/subtract 1.
32101 const lhs_is_signed = if (maybe_lhs_val) |lhs_val|
32102 !lhs_val.compareAllWithZero(.gte, zcu)
32103 else
32104 (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu));
32105 const rhs_is_signed = if (maybe_rhs_val) |rhs_val|
32106 !rhs_val.compareAllWithZero(.gte, zcu)
32107 else
32108 (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu));
32109 const dest_int_is_signed = lhs_is_signed or rhs_is_signed;
32110
32111 var dest_float_type: ?Type = null;
32112
32113 var lhs_bits: usize = undefined;
32114 if (maybe_lhs_val) |lhs_val| {
32115 if (!rhs_is_signed) {
32116 switch (Value.order(lhs_val, .zero_comptime_int, zcu)) {
32117 .gt => {},
32118 .eq => switch (op) { // LHS = 0, RHS is unsigned
32119 .lte => return .bool_true,
32120 .gt => return .bool_false,
32121 else => {},
32122 },
32123 .lt => switch (op) { // LHS < 0, RHS is unsigned
32124 .neq, .lt, .lte => return .bool_true,
32125 .eq, .gt, .gte => return .bool_false,
32126 },
32127 }
32128 }
32129 if (lhs_is_float) {
32130 const float = lhs_val.toFloat(f128, zcu);
32131 var big_int: std.math.big.int.Mutable = .{
32132 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)),
32133 .len = undefined,
32134 .positive = undefined,
32135 };
32136 switch (big_int.setFloat(float, .away)) {
32137 .inexact => switch (op) {
32138 .eq => return .bool_false,
32139 .neq => return .bool_true,
32140 else => {},
32141 },
32142 .exact => {},
32143 }
32144 lhs_bits = big_int.toConst().bitCountTwosComp();
32145 } else {
32146 lhs_bits = lhs_val.intBitCountTwosComp(zcu);
32147 }
32148 lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed);
32149 } else if (lhs_is_float) {
32150 dest_float_type = lhs_ty;
32151 } else {
32152 const int_info = lhs_ty.intInfo(zcu);
32153 lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
32154 }
32155
32156 var rhs_bits: usize = undefined;
32157 if (maybe_rhs_val) |rhs_val| {
32158 if (!lhs_is_signed) {
32159 switch (Value.order(rhs_val, .zero_comptime_int, zcu)) {
32160 .gt => {},
32161 .eq => switch (op) { // RHS = 0, LHS is unsigned
32162 .gte => return .bool_true,
32163 .lt => return .bool_false,
32164 else => {},
32165 },
32166 .lt => switch (op) { // RHS < 0, LHS is unsigned
32167 .neq, .gt, .gte => return .bool_true,
32168 .eq, .lt, .lte => return .bool_false,
32169 },
32170 }
32171 }
32172 if (rhs_is_float) {
32173 const float = rhs_val.toFloat(f128, zcu);
32174 var big_int: std.math.big.int.Mutable = .{
32175 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)),
32176 .len = undefined,
32177 .positive = undefined,
32178 };
32179 switch (big_int.setFloat(float, .away)) {
32180 .inexact => switch (op) {
32181 .eq => return .bool_false,
32182 .neq => return .bool_true,
32183 else => {},
32184 },
32185 .exact => {},
32186 }
32187 rhs_bits = big_int.toConst().bitCountTwosComp();
32188 } else {
32189 rhs_bits = rhs_val.intBitCountTwosComp(zcu);
32190 }
32191 rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed);
32192 } else if (rhs_is_float) {
32193 dest_float_type = rhs_ty;
32194 } else {
32195 const int_info = rhs_ty.intInfo(zcu);
32196 rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed);
32197 }
32198
32199 const dest_ty = if (dest_float_type) |ft| ft else blk: {
32200 const max_bits = @max(lhs_bits, rhs_bits);
32201 const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits});
32202 const signedness: std.lang.Signedness = if (dest_int_is_signed) .signed else .unsigned;
32203 break :blk try pt.intType(signedness, casted_bits);
32204 };
32205 const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src);
32206 const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src);
32207
32208 return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs);
32209}
32210
32211/// Asserts that LHS value is an int or comptime int and not undefined, and
32212/// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to
32213/// determine whether `op` has a guaranteed result.
32214/// If it cannot be determined, returns null.
32215/// Otherwise returns a bool for the guaranteed comparison operation.
32216fn compareIntsOnlyPossibleResult(
32217 sema: *Sema,
32218 lhs_val: Value,
32219 op: std.math.CompareOperator,
32220 rhs_ty: Type,
32221) Allocator.Error!?bool {
32222 const pt = sema.pt;
32223 const zcu = pt.zcu;
32224
32225 const min_rhs = try rhs_ty.minInt(pt, rhs_ty);
32226 const max_rhs = try rhs_ty.maxInt(pt, rhs_ty);
32227
32228 if (min_rhs.toIntern() == max_rhs.toIntern()) {
32229 // RHS is effectively comptime-known.
32230 return Value.compareHetero(lhs_val, op, min_rhs, zcu);
32231 }
32232
32233 const against_min = lhs_val.order(min_rhs, zcu);
32234 const against_max = lhs_val.order(max_rhs, zcu);
32235
32236 switch (op) {
32237 .eq => {
32238 if (against_min.compare(.lt)) return false;
32239 if (against_max.compare(.gt)) return false;
32240 },
32241 .neq => {
32242 if (against_min.compare(.lt)) return true;
32243 if (against_max.compare(.gt)) return true;
32244 },
32245 .lt => {
32246 if (against_min.compare(.lt)) return true;
32247 if (against_max.compare(.gte)) return false;
32248 },
32249 .gt => {
32250 if (against_max.compare(.gt)) return true;
32251 if (against_min.compare(.lte)) return false;
32252 },
32253 .lte => {
32254 if (against_min.compare(.lte)) return true;
32255 if (against_max.compare(.gt)) return false;
32256 },
32257 .gte => {
32258 if (against_max.compare(.gte)) return true;
32259 if (against_min.compare(.lt)) return false;
32260 },
32261 }
32262
32263 return null;
32264}
32265
32266/// Asserts that lhs and rhs types are both vectors.
32267fn cmpVector(
32268 sema: *Sema,
32269 block: *Block,
32270 src: LazySrcLoc,
32271 lhs: Air.Inst.Ref,
32272 rhs: Air.Inst.Ref,
32273 op: std.math.CompareOperator,
32274 lhs_src: LazySrcLoc,
32275 rhs_src: LazySrcLoc,
32276) CompileError!Air.Inst.Ref {
32277 const pt = sema.pt;
32278 const zcu = pt.zcu;
32279 const lhs_ty = sema.typeOf(lhs);
32280 const rhs_ty = sema.typeOf(rhs);
32281 assert(lhs_ty.zigTypeTag(zcu) == .vector);
32282 assert(rhs_ty.zigTypeTag(zcu) == .vector);
32283 try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
32284
32285 const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } });
32286 const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src);
32287 const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src);
32288
32289 const result_ty = try pt.vectorType(.{
32290 .len = lhs_ty.vectorLen(zcu),
32291 .child = .bool_type,
32292 });
32293
32294 const maybe_lhs_val = sema.resolveValue(casted_lhs);
32295 const maybe_rhs_val = sema.resolveValue(casted_rhs);
32296 if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
32297 if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty);
32298
32299 const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: {
32300 if (maybe_rhs_val) |rhs_val| {
32301 const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty);
32302 return Air.internedToRef(cmp_val.toIntern());
32303 } else break :src rhs_src;
32304 } else lhs_src;
32305
32306 try sema.requireRuntimeBlock(block, src, runtime_src);
32307 return block.addCmpVector(casted_lhs, casted_rhs, op);
32308}
32309
32310fn wrapOptional(
32311 sema: *Sema,
32312 block: *Block,
32313 dest_ty: Type,
32314 inst: Air.Inst.Ref,
32315 inst_src: LazySrcLoc,
32316) !Air.Inst.Ref {
32317 if (sema.resolveValue(inst)) |val| {
32318 return Air.internedToRef((try sema.pt.intern(.{ .opt = .{
32319 .ty = dest_ty.toIntern(),
32320 .val = val.toIntern(),
32321 } })));
32322 }
32323
32324 try sema.requireRuntimeBlock(block, inst_src, null);
32325 return block.addTyOp(.wrap_optional, dest_ty, inst);
32326}
32327
32328fn wrapErrorUnionPayload(
32329 sema: *Sema,
32330 block: *Block,
32331 dest_ty: Type,
32332 inst: Air.Inst.Ref,
32333 inst_src: LazySrcLoc,
32334) !Air.Inst.Ref {
32335 const pt = sema.pt;
32336 const zcu = pt.zcu;
32337 const dest_payload_ty = dest_ty.errorUnionPayload(zcu);
32338 const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false });
32339 if (sema.resolveValue(coerced)) |val| {
32340 return Air.internedToRef((try pt.intern(.{ .error_union = .{
32341 .ty = dest_ty.toIntern(),
32342 .val = .{ .payload = val.toIntern() },
32343 } })));
32344 }
32345 try sema.requireRuntimeBlock(block, inst_src, null);
32346 return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced);
32347}
32348
32349fn wrapErrorUnionSet(
32350 sema: *Sema,
32351 block: *Block,
32352 dest_ty: Type,
32353 inst: Air.Inst.Ref,
32354 inst_src: LazySrcLoc,
32355) !Air.Inst.Ref {
32356 const pt = sema.pt;
32357 const zcu = pt.zcu;
32358 const ip = &zcu.intern_pool;
32359 const dest_err_set_ty = dest_ty.errorUnionSet(zcu);
32360 const coerced = try sema.coerceExtra(block, dest_err_set_ty, inst, inst_src, .{ .report_err = false });
32361 if (try sema.resolveDefinedValue(block, inst_src, coerced)) |error_val| {
32362 return .fromIntern(try pt.intern(.{ .error_union = .{
32363 .ty = dest_ty.toIntern(),
32364 .val = .{ .err_name = ip.indexToKey(error_val.toIntern()).err.name },
32365 } }));
32366 } else {
32367 return block.addTyOp(.wrap_errunion_err, dest_ty, coerced);
32368 }
32369}
32370
32371/// Returns the enum tag value for the active tag of a tagged union value.
32372///
32373/// Asserts that the type of `un` is a tagged union type.
32374fn unionToTag(sema: *Sema, block: *Block, un: Air.Inst.Ref) !Air.Inst.Ref {
32375 const pt = sema.pt;
32376 const zcu = pt.zcu;
32377 const ip = &zcu.intern_pool;
32378 const union_obj = ip.loadUnionType(sema.typeOf(un).toIntern());
32379 assert(union_obj.tag_usage == .tagged);
32380 if (sema.resolveValue(un)) |un_val| {
32381 return .fromValue(un_val.unionTag(zcu).?);
32382 }
32383 const enum_tag_ty: Type = .fromInterned(union_obj.enum_tag_type);
32384 if (!union_obj.has_runtime_tag) {
32385 // This means that only one field is possible.
32386 const field_index = for (union_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| {
32387 const field_ty: Type = .fromInterned(field_ty_ip);
32388 if (field_ty.classify(zcu) != .no_possible_value) break field_index;
32389 } else unreachable;
32390 return .fromValue(try pt.enumValueFieldIndex(enum_tag_ty, @intCast(field_index)));
32391 }
32392 return block.addTyOp(.get_union_tag, enum_tag_ty, un);
32393}
32394
32395const PeerResolveStrategy = enum {
32396 /// The type is not known.
32397 /// If refined no further, this is equivalent to `exact`.
32398 unknown,
32399 /// The type may be an error set or error union.
32400 /// If refined no further, it is an error set.
32401 error_set,
32402 /// The type must be some error union.
32403 error_union,
32404 /// The type may be @TypeOf(null), an optional or a C pointer.
32405 /// If refined no further, it is @TypeOf(null).
32406 nullable,
32407 /// The type must be some optional or a C pointer.
32408 /// If refined no further, it is an optional.
32409 optional,
32410 /// The type must be either an array or a vector.
32411 /// If refined no further, it is an array.
32412 array,
32413 /// The type must be a vector.
32414 vector,
32415 /// The type must be a C pointer.
32416 c_ptr,
32417 /// The type must be a pointer (C or not).
32418 /// If refined no further, it is a non-C pointer.
32419 ptr,
32420 /// The type must be a function or a pointer to a function.
32421 /// If refined no further, it is a function.
32422 func,
32423 /// The type must be an enum literal, or some specific enum or union. Which one is decided
32424 /// afterwards based on the types in question.
32425 enum_or_union,
32426 /// The type must be some integer or float type.
32427 /// If refined no further, it is `comptime_int`.
32428 comptime_int,
32429 /// The type must be some float type.
32430 /// If refined no further, it is `comptime_float`.
32431 comptime_float,
32432 /// The type must be some float or fixed-width integer type.
32433 /// If refined no further, it is some fixed-width integer type.
32434 fixed_int,
32435 /// The type must be some fixed-width float type.
32436 fixed_float,
32437 /// The type must be a tuple.
32438 tuple,
32439 /// The peers must all be of the same type.
32440 exact,
32441
32442 /// Given two strategies, find a strategy that satisfies both, if one exists. If no such
32443 /// strategy exists, any strategy may be returned; an error will be emitted when the caller
32444 /// attempts to use the strategy to resolve the type.
32445 /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at
32446 /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy.
32447 fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy {
32448 // Our merging should be order-independent. Thus, even though the union order is arbitrary,
32449 // by sorting the tags and switching first on the smaller, we have half as many cases to
32450 // worry about (since we avoid the duplicates).
32451 const s0_is_a = @backingInt(a) <= @backingInt(b);
32452 const s0 = if (s0_is_a) a else b;
32453 const s1 = if (s0_is_a) b else a;
32454
32455 const ReasonMethod = enum {
32456 all_s0,
32457 all_s1,
32458 either,
32459 };
32460
32461 const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) {
32462 .unknown => .{ .all_s1, s1 },
32463 .error_set => switch (s1) {
32464 .error_set => .{ .either, .error_set },
32465 else => .{ .all_s0, .error_union },
32466 },
32467 .error_union => switch (s1) {
32468 .error_union => .{ .either, .error_union },
32469 else => .{ .all_s0, .error_union },
32470 },
32471 .nullable => switch (s1) {
32472 .nullable => .{ .either, .nullable },
32473 .c_ptr => .{ .all_s1, .c_ptr },
32474 else => .{ .all_s0, .optional },
32475 },
32476 .optional => switch (s1) {
32477 .optional => .{ .either, .optional },
32478 .c_ptr => .{ .all_s1, .c_ptr },
32479 else => .{ .all_s0, .optional },
32480 },
32481 .array => switch (s1) {
32482 .array => .{ .either, .array },
32483 .vector => .{ .all_s1, .vector },
32484 else => .{ .all_s0, .array },
32485 },
32486 .vector => switch (s1) {
32487 .vector => .{ .either, .vector },
32488 else => .{ .all_s0, .vector },
32489 },
32490 .c_ptr => switch (s1) {
32491 .c_ptr => .{ .either, .c_ptr },
32492 else => .{ .all_s0, .c_ptr },
32493 },
32494 .ptr => switch (s1) {
32495 .ptr => .{ .either, .ptr },
32496 else => .{ .all_s0, .ptr },
32497 },
32498 .func => switch (s1) {
32499 .func => .{ .either, .func },
32500 else => .{ .all_s1, s1 }, // doesn't override anything later
32501 },
32502 .enum_or_union => switch (s1) {
32503 .enum_or_union => .{ .either, .enum_or_union },
32504 else => .{ .all_s0, .enum_or_union },
32505 },
32506 .comptime_int => switch (s1) {
32507 .comptime_int => .{ .either, .comptime_int },
32508 else => .{ .all_s1, s1 }, // doesn't override anything later
32509 },
32510 .comptime_float => switch (s1) {
32511 .comptime_float => .{ .either, .comptime_float },
32512 else => .{ .all_s1, s1 }, // doesn't override anything later
32513 },
32514 .fixed_int => switch (s1) {
32515 .fixed_int => .{ .either, .fixed_int },
32516 else => .{ .all_s1, s1 }, // doesn't override anything later
32517 },
32518 .fixed_float => switch (s1) {
32519 .fixed_float => .{ .either, .fixed_float },
32520 else => .{ .all_s1, s1 }, // doesn't override anything later
32521 },
32522 .tuple => switch (s1) {
32523 .exact => .{ .all_s1, .exact },
32524 else => .{ .all_s0, .tuple },
32525 },
32526 .exact => .{ .all_s0, .exact },
32527 };
32528
32529 switch (reason_method) {
32530 .all_s0 => {
32531 if (!s0_is_a) {
32532 reason_peer.* = b_peer_idx;
32533 }
32534 },
32535 .all_s1 => {
32536 if (s0_is_a) {
32537 reason_peer.* = b_peer_idx;
32538 }
32539 },
32540 .either => {
32541 // Prefer the earliest peer
32542 reason_peer.* = @min(reason_peer.*, b_peer_idx);
32543 },
32544 }
32545
32546 return strat;
32547 }
32548
32549 fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy {
32550 return switch (ty.zigTypeTag(zcu)) {
32551 .type, .void, .bool, .@"opaque", .spirv, .frame, .@"anyframe" => .exact,
32552 .noreturn, .undefined => .unknown,
32553 .null => .nullable,
32554 .comptime_int => .comptime_int,
32555 .int => .fixed_int,
32556 .comptime_float => .comptime_float,
32557 .float => .fixed_float,
32558 .pointer => if (ty.ptrInfo(zcu).flags.size == .c) .c_ptr else .ptr,
32559 .array => .array,
32560 .vector => .vector,
32561 .optional => .optional,
32562 .error_set => .error_set,
32563 .error_union => .error_union,
32564 .enum_literal, .@"enum", .@"union" => .enum_or_union,
32565 .@"struct" => if (ty.isTuple(zcu)) .tuple else .exact,
32566 .@"fn" => .func,
32567 };
32568 }
32569};
32570
32571const PeerTypeCandidateSrc = union(enum) {
32572 /// Do not print out error notes for candidate sources
32573 none: void,
32574 /// When we want to know the the src of candidate i, look up at
32575 /// index i in this slice
32576 override: []const ?LazySrcLoc,
32577 /// resolvePeerTypes originates from a @TypeOf(...) call
32578 typeof_builtin_call_node_offset: std.zig.Ast.Node.Offset,
32579
32580 pub fn resolve(
32581 self: PeerTypeCandidateSrc,
32582 block: *Block,
32583 candidate_i: usize,
32584 ) ?LazySrcLoc {
32585 return switch (self) {
32586 .none => null,
32587 .override => |candidate_srcs| if (candidate_i >= candidate_srcs.len)
32588 null
32589 else
32590 candidate_srcs[candidate_i],
32591 .typeof_builtin_call_node_offset => |node_offset| block.builtinCallArgSrc(node_offset, @intCast(candidate_i)),
32592 };
32593 }
32594};
32595
32596const PeerResolveResult = union(enum) {
32597 /// The peer type resolution was successful, and resulted in the given type.
32598 success: Type,
32599 /// There was some generic conflict between two peers.
32600 conflict: struct {
32601 peer_idx_a: usize,
32602 peer_idx_b: usize,
32603 },
32604 /// There was an error when resolving the type of a struct or tuple field.
32605 field_error: struct {
32606 /// The name of the field which caused the failure.
32607 field_name: InternPool.NullTerminatedString,
32608 /// The type of this field in each peer.
32609 field_types: []Type,
32610 /// The error from resolving the field type. Guaranteed not to be `success`.
32611 sub_result: *PeerResolveResult,
32612 },
32613
32614 fn report(
32615 result: PeerResolveResult,
32616 sema: *Sema,
32617 block: *Block,
32618 src: LazySrcLoc,
32619 instructions: []const Air.Inst.Ref,
32620 candidate_srcs: PeerTypeCandidateSrc,
32621 ) !*Zcu.ErrorMsg {
32622 const pt = sema.pt;
32623
32624 var opt_msg: ?*Zcu.ErrorMsg = null;
32625 errdefer if (opt_msg) |msg| msg.destroy(sema.gpa);
32626
32627 // If we mention fields we'll want to include field types, so put peer types in a buffer
32628 var peer_tys = try sema.arena.alloc(Type, instructions.len);
32629 for (peer_tys, instructions) |*ty, inst| {
32630 ty.* = sema.typeOf(inst);
32631 }
32632
32633 var cur = result;
32634 while (true) {
32635 var conflict_idx: [2]usize = undefined;
32636
32637 switch (cur) {
32638 .success => unreachable,
32639 .conflict => |conflict| {
32640 // Fall through to two-peer conflict handling below
32641 conflict_idx = .{
32642 conflict.peer_idx_a,
32643 conflict.peer_idx_b,
32644 };
32645 },
32646 .field_error => |field_error| {
32647 const fmt = "struct field '{f}' has conflicting types";
32648 const args = .{field_error.field_name.fmt(&pt.zcu.intern_pool)};
32649 if (opt_msg) |msg| {
32650 try sema.errNote(src, msg, fmt, args);
32651 } else {
32652 opt_msg = try sema.errMsg(src, fmt, args);
32653 }
32654
32655 // Continue on to child error
32656 cur = field_error.sub_result.*;
32657 peer_tys = field_error.field_types;
32658 continue;
32659 },
32660 }
32661
32662 // This is the path for reporting a generic conflict between two peers.
32663
32664 if (conflict_idx[1] < conflict_idx[0]) {
32665 // b comes first in source, so it's better if it comes first in the error
32666 std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]);
32667 }
32668
32669 const conflict_tys: [2]Type = .{
32670 peer_tys[conflict_idx[0]],
32671 peer_tys[conflict_idx[1]],
32672 };
32673 const conflict_srcs: [2]?LazySrcLoc = .{
32674 candidate_srcs.resolve(block, conflict_idx[0]),
32675 candidate_srcs.resolve(block, conflict_idx[1]),
32676 };
32677
32678 const fmt = "incompatible types: '{f}' and '{f}'";
32679 const args = .{
32680 conflict_tys[0].fmt(pt),
32681 conflict_tys[1].fmt(pt),
32682 };
32683 const msg = if (opt_msg) |msg| msg: {
32684 try sema.errNote(src, msg, fmt, args);
32685 break :msg msg;
32686 } else msg: {
32687 const msg = try sema.errMsg(src, fmt, args);
32688 opt_msg = msg;
32689 break :msg msg;
32690 };
32691
32692 if (conflict_srcs[0]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[0].fmt(pt)});
32693 if (conflict_srcs[1]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[1].fmt(pt)});
32694
32695 // No child error
32696 break;
32697 }
32698
32699 return opt_msg.?;
32700 }
32701};
32702
32703fn resolvePeerTypes(
32704 sema: *Sema,
32705 block: *Block,
32706 src: LazySrcLoc,
32707 instructions: []const Air.Inst.Ref,
32708 candidate_srcs: PeerTypeCandidateSrc,
32709) !Type {
32710 switch (instructions.len) {
32711 0 => return .noreturn,
32712 1 => return sema.typeOf(instructions[0]),
32713 else => {},
32714 }
32715
32716 // Fast path: check if everything has the same type to bypass the main PTR logic.
32717 same_type: {
32718 const ty = sema.typeOf(instructions[0]);
32719 for (instructions[1..]) |inst| {
32720 if (sema.typeOf(inst).toIntern() != ty.toIntern()) {
32721 break :same_type;
32722 }
32723 }
32724 return ty;
32725 }
32726
32727 const peer_tys = try sema.arena.alloc(?Type, instructions.len);
32728 const peer_vals = try sema.arena.alloc(?Value, instructions.len);
32729
32730 for (instructions, peer_tys, peer_vals) |inst, *ty, *val| {
32731 ty.* = sema.typeOf(inst);
32732 val.* = sema.resolveValue(inst);
32733 }
32734
32735 switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) {
32736 .success => |ty| return ty,
32737 else => |result| {
32738 const msg = try result.report(sema, block, src, instructions, candidate_srcs);
32739 return sema.failWithOwnedErrorMsg(block, msg);
32740 },
32741 }
32742}
32743
32744fn resolvePeerTypesInner(
32745 sema: *Sema,
32746 block: *Block,
32747 src: LazySrcLoc,
32748 peer_tys: []?Type,
32749 peer_vals: []?Value,
32750) !PeerResolveResult {
32751 const pt = sema.pt;
32752 const zcu = pt.zcu;
32753 const comp = zcu.comp;
32754 const gpa = comp.gpa;
32755 const io = comp.io;
32756 const ip = &zcu.intern_pool;
32757
32758 var strat_reason: usize = 0;
32759 var s: PeerResolveStrategy = .unknown;
32760 for (peer_tys, 0..) |opt_ty, i| {
32761 const ty = opt_ty orelse continue;
32762 s = s.merge(PeerResolveStrategy.select(ty, zcu), &strat_reason, i);
32763 }
32764
32765 if (s == .unknown) {
32766 // The whole thing was noreturn or undefined - try to do an exact match
32767 s = .exact;
32768 } else {
32769 // There was something other than noreturn and undefined, so we can ignore those peers
32770 for (peer_tys) |*ty_ptr| {
32771 const ty = ty_ptr.* orelse continue;
32772 switch (ty.zigTypeTag(zcu)) {
32773 .noreturn, .undefined => ty_ptr.* = null,
32774 else => {},
32775 }
32776 }
32777 }
32778
32779 const target = zcu.getTarget();
32780
32781 switch (s) {
32782 .unknown => unreachable,
32783
32784 .error_set => {
32785 var final_set: ?Type = null;
32786 for (peer_tys, 0..) |opt_ty, i| {
32787 const ty = opt_ty orelse continue;
32788 if (ty.zigTypeTag(zcu) != .error_set) return .{ .conflict = .{
32789 .peer_idx_a = strat_reason,
32790 .peer_idx_b = i,
32791 } };
32792 if (final_set) |cur_set| {
32793 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty);
32794 } else {
32795 final_set = ty;
32796 }
32797 }
32798 return .{ .success = final_set.? };
32799 },
32800
32801 .error_union => {
32802 var final_set: ?Type = null;
32803 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
32804 const ty = ty_ptr.* orelse continue;
32805 const set_ty = switch (ty.zigTypeTag(zcu)) {
32806 .error_set => blk: {
32807 ty_ptr.* = null; // no payload to decide on
32808 val_ptr.* = null;
32809 break :blk ty;
32810 },
32811 .error_union => blk: {
32812 const set_ty = ty.errorUnionSet(zcu);
32813 ty_ptr.* = ty.errorUnionPayload(zcu);
32814 if (val_ptr.*) |eu_val| switch (ip.indexToKey(eu_val.toIntern())) {
32815 .error_union => |eu| switch (eu.val) {
32816 .payload => |payload_ip| val_ptr.* = Value.fromInterned(payload_ip),
32817 .err_name => val_ptr.* = null,
32818 },
32819 .undef => val_ptr.* = Value.fromInterned(try pt.intern(.{ .undef = ty_ptr.*.?.toIntern() })),
32820 else => unreachable,
32821 };
32822 break :blk set_ty;
32823 },
32824 else => continue, // whole type is the payload
32825 };
32826 if (final_set) |cur_set| {
32827 final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty);
32828 } else {
32829 final_set = set_ty;
32830 }
32831 }
32832 assert(final_set != null);
32833 const final_payload = switch (try sema.resolvePeerTypesInner(
32834 block,
32835 src,
32836 peer_tys,
32837 peer_vals,
32838 )) {
32839 .success => |ty| ty,
32840 else => |result| return result,
32841 };
32842 return .{ .success = try pt.errorUnionType(final_set.?, final_payload) };
32843 },
32844
32845 .nullable => {
32846 for (peer_tys, 0..) |opt_ty, i| {
32847 const ty = opt_ty orelse continue;
32848 if (!ty.eql(.null)) return .{ .conflict = .{
32849 .peer_idx_a = strat_reason,
32850 .peer_idx_b = i,
32851 } };
32852 }
32853 return .{ .success = .null };
32854 },
32855
32856 .optional => {
32857 for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| {
32858 const ty = ty_ptr.* orelse continue;
32859 switch (ty.zigTypeTag(zcu)) {
32860 .null => {
32861 ty_ptr.* = null;
32862 val_ptr.* = null;
32863 },
32864 .optional => {
32865 ty_ptr.* = ty.optionalChild(zcu);
32866 if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(zcu)) opt_val.optionalValue(zcu) else null;
32867 },
32868 else => {},
32869 }
32870 }
32871 const child_ty = switch (try sema.resolvePeerTypesInner(
32872 block,
32873 src,
32874 peer_tys,
32875 peer_vals,
32876 )) {
32877 .success => |ty| ty,
32878 else => |result| return result,
32879 };
32880 return .{ .success = try pt.optionalType(child_ty.toIntern()) };
32881 },
32882
32883 .array => {
32884 // Index of the first non-null peer
32885 var opt_first_idx: ?usize = null;
32886 // Index of the first array or vector peer (i.e. not a tuple)
32887 var opt_first_arr_idx: ?usize = null;
32888 // Set to non-null once we see any peer, even a tuple
32889 var len: u64 = undefined;
32890 var sentinel: ?Value = undefined;
32891 // Only set once we see a non-tuple peer
32892 var elem_ty: Type = undefined;
32893
32894 for (peer_tys, 0..) |*ty_ptr, i| {
32895 const ty = ty_ptr.* orelse continue;
32896
32897 if (!ty.isArrayOrVector(zcu)) {
32898 // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced.
32899 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
32900 .peer_idx_a = strat_reason,
32901 .peer_idx_b = i,
32902 } };
32903
32904 if (opt_first_idx) |first_idx| {
32905 if (arr_like.len != len) return .{ .conflict = .{
32906 .peer_idx_a = first_idx,
32907 .peer_idx_b = i,
32908 } };
32909 } else {
32910 opt_first_idx = i;
32911 len = arr_like.len;
32912 }
32913
32914 sentinel = null;
32915
32916 continue;
32917 }
32918
32919 const first_arr_idx = opt_first_arr_idx orelse {
32920 if (opt_first_idx == null) {
32921 opt_first_idx = i;
32922 len = ty.arrayLen(zcu);
32923 sentinel = ty.sentinel(zcu);
32924 }
32925 opt_first_arr_idx = i;
32926 elem_ty = ty.childType(zcu);
32927 continue;
32928 };
32929
32930 if (ty.arrayLen(zcu) != len) return .{ .conflict = .{
32931 .peer_idx_a = first_arr_idx,
32932 .peer_idx_b = i,
32933 } };
32934
32935 const peer_elem_ty = ty.childType(zcu);
32936 if (!peer_elem_ty.eql(elem_ty)) coerce: {
32937 const peer_elem_coerces_to_elem =
32938 try sema.coerceInMemoryAllowed(block, elem_ty, peer_elem_ty, false, zcu.getTarget(), src, src, null);
32939 if (peer_elem_coerces_to_elem == .ok) {
32940 break :coerce;
32941 }
32942
32943 const elem_coerces_to_peer_elem =
32944 try sema.coerceInMemoryAllowed(block, peer_elem_ty, elem_ty, false, zcu.getTarget(), src, src, null);
32945 if (elem_coerces_to_peer_elem == .ok) {
32946 elem_ty = peer_elem_ty;
32947 break :coerce;
32948 }
32949
32950 return .{ .conflict = .{
32951 .peer_idx_a = first_arr_idx,
32952 .peer_idx_b = i,
32953 } };
32954 }
32955
32956 if (sentinel) |cur_sent| {
32957 if (ty.sentinel(zcu)) |peer_sent| {
32958 if (!peer_sent.eql(cur_sent, elem_ty, zcu)) sentinel = null;
32959 } else {
32960 sentinel = null;
32961 }
32962 }
32963 }
32964
32965 // There should always be at least one array or vector peer
32966 assert(opt_first_arr_idx != null);
32967
32968 return .{ .success = try pt.arrayType(.{
32969 .len = len,
32970 .child = elem_ty.toIntern(),
32971 .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none,
32972 }) };
32973 },
32974
32975 .vector => {
32976 var len: ?u64 = null;
32977 var first_idx: usize = undefined;
32978 for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| {
32979 const ty = ty_ptr.* orelse continue;
32980
32981 if (!ty.isArrayOrVector(zcu)) {
32982 // Allow tuples of the correct length
32983 const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{
32984 .peer_idx_a = strat_reason,
32985 .peer_idx_b = i,
32986 } };
32987
32988 if (len) |expect_len| {
32989 if (arr_like.len != expect_len) return .{ .conflict = .{
32990 .peer_idx_a = first_idx,
32991 .peer_idx_b = i,
32992 } };
32993 } else {
32994 len = arr_like.len;
32995 first_idx = i;
32996 }
32997
32998 // Tuples won't participate in the child type resolution. We'll resolve without
32999 // them, and if the tuples have a bad type, we'll get a coercion error later.
33000 ty_ptr.* = null;
33001 val_ptr.* = null;
33002
33003 continue;
33004 }
33005
33006 if (len) |expect_len| {
33007 if (ty.arrayLen(zcu) != expect_len) return .{ .conflict = .{
33008 .peer_idx_a = first_idx,
33009 .peer_idx_b = i,
33010 } };
33011 } else {
33012 len = ty.arrayLen(zcu);
33013 first_idx = i;
33014 }
33015
33016 ty_ptr.* = ty.childType(zcu);
33017 val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR
33018 }
33019
33020 const child_ty = switch (try sema.resolvePeerTypesInner(
33021 block,
33022 src,
33023 peer_tys,
33024 peer_vals,
33025 )) {
33026 .success => |ty| ty,
33027 else => |result| return result,
33028 };
33029
33030 return .{ .success = try pt.vectorType(.{
33031 .len = @intCast(len.?),
33032 .child = child_ty.toIntern(),
33033 }) };
33034 },
33035
33036 .c_ptr => {
33037 var opt_ptr_info: ?InternPool.Key.PtrType = null;
33038 var first_idx: usize = undefined;
33039 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
33040 const ty = opt_ty orelse continue;
33041 switch (ty.zigTypeTag(zcu)) {
33042 .comptime_int => continue, // comptime-known integers can always coerce to C pointers
33043 .int => {
33044 if (opt_val != null) {
33045 // Always allow the coercion for comptime-known ints
33046 continue;
33047 } else {
33048 // Runtime-known, so check if the type is no bigger than a usize
33049 const ptr_bits = target.ptrBitWidth();
33050 const bits = ty.intInfo(zcu).bits;
33051 if (bits <= ptr_bits) continue;
33052 }
33053 },
33054 .null => continue,
33055 else => {},
33056 }
33057
33058 if (!ty.isPtrAtRuntime(zcu)) return .{ .conflict = .{
33059 .peer_idx_a = strat_reason,
33060 .peer_idx_b = i,
33061 } };
33062
33063 // Goes through optionals
33064 const peer_info = ty.ptrInfo(zcu);
33065
33066 var ptr_info = opt_ptr_info orelse {
33067 opt_ptr_info = peer_info;
33068 opt_ptr_info.?.flags.size = .c;
33069 first_idx = i;
33070 continue;
33071 };
33072
33073 // Try peer -> cur, then cur -> peer
33074 ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) orelse {
33075 return .{ .conflict = .{
33076 .peer_idx_a = first_idx,
33077 .peer_idx_b = i,
33078 } };
33079 }).toIntern();
33080
33081 if (ptr_info.sentinel != .none and peer_info.sentinel != .none) {
33082 const peer_sent = try ip.getCoerced(gpa, io, pt.tid, ptr_info.sentinel, ptr_info.child);
33083 const ptr_sent = try ip.getCoerced(gpa, io, pt.tid, peer_info.sentinel, ptr_info.child);
33084 if (ptr_sent == peer_sent) {
33085 ptr_info.sentinel = ptr_sent;
33086 } else {
33087 ptr_info.sentinel = .none;
33088 }
33089 } else {
33090 ptr_info.sentinel = .none;
33091 }
33092
33093 ptr_info.flags.alignment = a: {
33094 // If both alignments are implicit, the result alignment is implicit.
33095 // e.g. '[*c]u32' + '[*c]c_uint' -> '[*c]u32'
33096 if (ptr_info.flags.alignment == .none and peer_info.flags.alignment == .none) {
33097 break :a .none;
33098 }
33099 // Otherwise (if either alignment is explicit), the result alignment is explicit.
33100 // e.g. '[*c]u32' + '[*c]align(4) c_uint' -> '[*c]align(4) u32'
33101 const cur_align = switch (ptr_info.flags.alignment) {
33102 .none => Type.fromInterned(ptr_info.child).abiAlignment(zcu),
33103 else => ptr_info.flags.alignment,
33104 };
33105 const new_align = switch (peer_info.flags.alignment) {
33106 .none => Type.fromInterned(peer_info.child).abiAlignment(zcu),
33107 else => peer_info.flags.alignment,
33108 };
33109 break :a .minStrict(cur_align, new_align);
33110 };
33111 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
33112 return .{ .conflict = .{
33113 .peer_idx_a = first_idx,
33114 .peer_idx_b = i,
33115 } };
33116 }
33117
33118 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
33119 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
33120 {
33121 return .{ .conflict = .{
33122 .peer_idx_a = first_idx,
33123 .peer_idx_b = i,
33124 } };
33125 }
33126
33127 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
33128 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
33129
33130 opt_ptr_info = ptr_info;
33131 }
33132 return .{ .success = try pt.ptrType(opt_ptr_info.?) };
33133 },
33134
33135 .ptr => {
33136 // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only
33137 // if there were no actual slices. Else, we want the slice index to report a conflict.
33138 var opt_slice_idx: ?usize = null;
33139
33140 var opt_ptr_info: ?InternPool.Key.PtrType = null;
33141 var first_idx: usize = undefined;
33142 var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error
33143
33144 for (peer_tys, 0..) |opt_ty, i| {
33145 const ty = opt_ty orelse continue;
33146 const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(zcu)) {
33147 .pointer => ty.ptrInfo(zcu),
33148 .@"fn" => .{
33149 .child = ty.toIntern(),
33150 .flags = .{
33151 .address_space = target_util.defaultAddressSpace(target, .global_constant),
33152 },
33153 },
33154 else => return .{ .conflict = .{
33155 .peer_idx_a = strat_reason,
33156 .peer_idx_b = i,
33157 } },
33158 };
33159
33160 switch (peer_info.flags.size) {
33161 .one, .many => {},
33162 .slice => opt_slice_idx = i,
33163 .c => return .{ .conflict = .{
33164 .peer_idx_a = strat_reason,
33165 .peer_idx_b = i,
33166 } },
33167 }
33168
33169 var ptr_info = opt_ptr_info orelse {
33170 opt_ptr_info = peer_info;
33171 first_idx = i;
33172 continue;
33173 };
33174
33175 other_idx = i;
33176
33177 // We want to return this in a lot of cases, so alias it here for convenience
33178 const generic_err: PeerResolveResult = .{ .conflict = .{
33179 .peer_idx_a = first_idx,
33180 .peer_idx_b = i,
33181 } };
33182
33183 ptr_info.flags.alignment = a: {
33184 // If both alignments are implicit, the result alignment is implicit.
33185 // e.g. '*u32' + '*c_uint' -> '*u32'
33186 if (ptr_info.flags.alignment == .none and peer_info.flags.alignment == .none) {
33187 break :a .none;
33188 }
33189 // Otherwise (if either alignment is explicit), the result alignment is explicit.
33190 // e.g. '*u32' + '*align(4) c_uint' -> '*align(4) u32'
33191 const cur_align = switch (ptr_info.flags.alignment) {
33192 .none => Type.fromInterned(ptr_info.child).abiAlignment(zcu),
33193 else => ptr_info.flags.alignment,
33194 };
33195 const new_align = switch (peer_info.flags.alignment) {
33196 .none => Type.fromInterned(peer_info.child).abiAlignment(zcu),
33197 else => peer_info.flags.alignment,
33198 };
33199 break :a .minStrict(cur_align, new_align);
33200 };
33201
33202 if (ptr_info.flags.address_space != peer_info.flags.address_space) {
33203 return generic_err;
33204 }
33205
33206 if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or
33207 ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size)
33208 {
33209 return generic_err;
33210 }
33211
33212 ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const;
33213 ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile;
33214 ptr_info.flags.is_allowzero = ptr_info.flags.is_allowzero or peer_info.flags.is_allowzero;
33215
33216 const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) {
33217 .one => switch (ip.indexToKey(peer_info.child)) {
33218 .array_type => |array_type| array_type.sentinel,
33219 else => .none,
33220 },
33221 .many, .slice => peer_info.sentinel,
33222 .c => unreachable,
33223 };
33224
33225 const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) {
33226 .one => switch (ip.indexToKey(ptr_info.child)) {
33227 .array_type => |array_type| array_type.sentinel,
33228 else => .none,
33229 },
33230 .many, .slice => ptr_info.sentinel,
33231 .c => unreachable,
33232 };
33233
33234 // We abstract array handling slightly so that tuple pointers can work like array pointers
33235 const peer_pointee_array = sema.typeIsArrayLike(.fromInterned(peer_info.child));
33236 const cur_pointee_array = sema.typeIsArrayLike(.fromInterned(ptr_info.child));
33237
33238 // This switch is just responsible for deciding the size and pointee (not including
33239 // single-pointer array sentinel).
33240 good: {
33241 switch (peer_info.flags.size) {
33242 .one => switch (ptr_info.flags.size) {
33243 .one => {
33244 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
33245 ptr_info.child = pointee.toIntern();
33246 break :good;
33247 }
33248
33249 const cur_arr = cur_pointee_array orelse return generic_err;
33250 const peer_arr = peer_pointee_array orelse return generic_err;
33251
33252 if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| {
33253 // *[n:x]T + *[n:y]T = *[n]T
33254 if (cur_arr.len == peer_arr.len) {
33255 ptr_info.child = (try pt.arrayType(.{
33256 .len = cur_arr.len,
33257 .child = elem_ty.toIntern(),
33258 })).toIntern();
33259 break :good;
33260 }
33261 // *[a]T + *[b]T = []T
33262 ptr_info.flags.size = .slice;
33263 ptr_info.child = elem_ty.toIntern();
33264 break :good;
33265 }
33266
33267 if (peer_arr.elem_ty.toIntern() == .noreturn_type) {
33268 // *struct{} + *[a]T = []T
33269 ptr_info.flags.size = .slice;
33270 ptr_info.child = cur_arr.elem_ty.toIntern();
33271 break :good;
33272 }
33273
33274 if (cur_arr.elem_ty.toIntern() == .noreturn_type) {
33275 // *[a]T + *struct{} = []T
33276 ptr_info.flags.size = .slice;
33277 ptr_info.child = peer_arr.elem_ty.toIntern();
33278 break :good;
33279 }
33280
33281 return generic_err;
33282 },
33283 .many => {
33284 // Only works for *[n]T + [*]T -> [*]T
33285 const arr = peer_pointee_array orelse return generic_err;
33286 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| {
33287 ptr_info.child = pointee.toIntern();
33288 break :good;
33289 }
33290 if (arr.elem_ty.toIntern() == .noreturn_type) {
33291 // *struct{} + [*]T -> [*]T
33292 break :good;
33293 }
33294 return generic_err;
33295 },
33296 .slice => {
33297 // Only works for *[n]T + []T -> []T
33298 const arr = peer_pointee_array orelse return generic_err;
33299 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| {
33300 ptr_info.child = pointee.toIntern();
33301 break :good;
33302 }
33303 if (arr.elem_ty.toIntern() == .noreturn_type) {
33304 // *struct{} + []T -> []T
33305 break :good;
33306 }
33307 return generic_err;
33308 },
33309 .c => unreachable,
33310 },
33311 .many => switch (ptr_info.flags.size) {
33312 .one => {
33313 // Only works for [*]T + *[n]T -> [*]T
33314 const arr = cur_pointee_array orelse return generic_err;
33315 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| {
33316 ptr_info.flags.size = .many;
33317 ptr_info.child = pointee.toIntern();
33318 break :good;
33319 }
33320 if (arr.elem_ty.toIntern() == .noreturn_type) {
33321 // [*]T + *struct{} -> [*]T
33322 ptr_info.flags.size = .many;
33323 ptr_info.child = peer_info.child;
33324 break :good;
33325 }
33326 return generic_err;
33327 },
33328 .many => {
33329 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
33330 ptr_info.child = pointee.toIntern();
33331 break :good;
33332 }
33333 return generic_err;
33334 },
33335 .slice => {
33336 // Only works if no peers are actually slices
33337 if (opt_slice_idx) |slice_idx| {
33338 return .{ .conflict = .{
33339 .peer_idx_a = slice_idx,
33340 .peer_idx_b = i,
33341 } };
33342 }
33343 // Okay, then works for [*]T + "[]T" -> [*]T
33344 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
33345 ptr_info.flags.size = .many;
33346 ptr_info.child = pointee.toIntern();
33347 break :good;
33348 }
33349 return generic_err;
33350 },
33351 .c => unreachable,
33352 },
33353 .slice => switch (ptr_info.flags.size) {
33354 .one => {
33355 // Only works for []T + *[n]T -> []T
33356 const arr = cur_pointee_array orelse return generic_err;
33357 if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| {
33358 ptr_info.flags.size = .slice;
33359 ptr_info.child = pointee.toIntern();
33360 break :good;
33361 }
33362 if (arr.elem_ty.toIntern() == .noreturn_type) {
33363 // []T + *struct{} -> []T
33364 ptr_info.flags.size = .slice;
33365 ptr_info.child = peer_info.child;
33366 break :good;
33367 }
33368 return generic_err;
33369 },
33370 .many => {
33371 // Impossible! (current peer is an actual slice)
33372 return generic_err;
33373 },
33374 .slice => {
33375 if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| {
33376 ptr_info.child = pointee.toIntern();
33377 break :good;
33378 }
33379 return generic_err;
33380 },
33381 .c => unreachable,
33382 },
33383 .c => unreachable,
33384 }
33385 }
33386
33387 const sentinel_ty = switch (ptr_info.flags.size) {
33388 .one => switch (ip.indexToKey(ptr_info.child)) {
33389 .array_type => |array_type| array_type.child,
33390 else => ptr_info.child,
33391 },
33392 .many, .slice, .c => ptr_info.child,
33393 };
33394
33395 sentinel: {
33396 no_sentinel: {
33397 if (peer_sentinel == .none) break :no_sentinel;
33398 if (cur_sentinel == .none) break :no_sentinel;
33399 const peer_sent_coerced = try ip.getCoerced(gpa, io, pt.tid, peer_sentinel, sentinel_ty);
33400 const cur_sent_coerced = try ip.getCoerced(gpa, io, pt.tid, cur_sentinel, sentinel_ty);
33401 if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel;
33402 // Sentinels match
33403 if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) {
33404 .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{
33405 .len = array_type.len,
33406 .child = array_type.child,
33407 .sentinel = cur_sent_coerced,
33408 })).toIntern(),
33409 else => unreachable,
33410 } else {
33411 ptr_info.sentinel = cur_sent_coerced;
33412 }
33413 break :sentinel;
33414 }
33415 // Clear existing sentinel
33416 ptr_info.sentinel = .none;
33417 if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) {
33418 .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{
33419 .len = array_type.len,
33420 .child = array_type.child,
33421 .sentinel = .none,
33422 })).toIntern(),
33423 else => {},
33424 };
33425 }
33426
33427 opt_ptr_info = ptr_info;
33428 }
33429
33430 // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance)
33431 // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to
33432 // coerce the empty struct to a specific type, but no peer provided one. We need to
33433 // detect this case and emit an error.
33434 const pointee = opt_ptr_info.?.child;
33435 switch (pointee) {
33436 .noreturn_type => return .{ .conflict = .{
33437 .peer_idx_a = first_idx,
33438 .peer_idx_b = other_idx,
33439 } },
33440 else => switch (ip.indexToKey(pointee)) {
33441 .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{
33442 .peer_idx_a = first_idx,
33443 .peer_idx_b = other_idx,
33444 } },
33445 else => {},
33446 },
33447 }
33448
33449 return .{ .success = try pt.ptrType(opt_ptr_info.?) };
33450 },
33451
33452 .func => {
33453 var opt_cur_ty: ?Type = null;
33454 var first_idx: usize = undefined;
33455 for (peer_tys, 0..) |opt_ty, i| {
33456 const ty = opt_ty orelse continue;
33457 const cur_ty = opt_cur_ty orelse {
33458 opt_cur_ty = ty;
33459 first_idx = i;
33460 continue;
33461 };
33462 if (ty.zigTypeTag(zcu) != .@"fn") return .{ .conflict = .{
33463 .peer_idx_a = strat_reason,
33464 .peer_idx_b = i,
33465 } };
33466 // ty -> cur_ty
33467 if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, false, target, src, src)) {
33468 continue;
33469 }
33470 // cur_ty -> ty
33471 if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, false, target, src, src)) {
33472 opt_cur_ty = ty;
33473 continue;
33474 }
33475 return .{ .conflict = .{
33476 .peer_idx_a = first_idx,
33477 .peer_idx_b = i,
33478 } };
33479 }
33480 return .{ .success = opt_cur_ty.? };
33481 },
33482
33483 .enum_or_union => {
33484 var opt_cur_ty: ?Type = null;
33485 // The peer index which gave the current type
33486 var cur_ty_idx: usize = undefined;
33487
33488 for (peer_tys, 0..) |opt_ty, i| {
33489 const ty = opt_ty orelse continue;
33490 switch (ty.zigTypeTag(zcu)) {
33491 .enum_literal, .@"enum", .@"union" => {},
33492 else => return .{ .conflict = .{
33493 .peer_idx_a = strat_reason,
33494 .peer_idx_b = i,
33495 } },
33496 }
33497 const cur_ty = opt_cur_ty orelse {
33498 opt_cur_ty = ty;
33499 cur_ty_idx = i;
33500 continue;
33501 };
33502
33503 // We want to return this in a lot of cases, so alias it here for convenience
33504 const generic_err: PeerResolveResult = .{ .conflict = .{
33505 .peer_idx_a = cur_ty_idx,
33506 .peer_idx_b = i,
33507 } };
33508
33509 switch (cur_ty.zigTypeTag(zcu)) {
33510 .enum_literal => {
33511 opt_cur_ty = ty;
33512 cur_ty_idx = i;
33513 },
33514 .@"enum" => switch (ty.zigTypeTag(zcu)) {
33515 .enum_literal => {},
33516 .@"enum" => {
33517 if (!ty.eql(cur_ty)) return generic_err;
33518 },
33519 .@"union" => {
33520 const tag_ty = ty.unionTagTypeHypothetical(zcu);
33521 if (!tag_ty.eql(cur_ty)) return generic_err;
33522 opt_cur_ty = ty;
33523 cur_ty_idx = i;
33524 },
33525 else => unreachable,
33526 },
33527 .@"union" => switch (ty.zigTypeTag(zcu)) {
33528 .enum_literal => {},
33529 .@"enum" => {
33530 const cur_tag_ty = cur_ty.unionTagTypeHypothetical(zcu);
33531 if (!ty.eql(cur_tag_ty)) return generic_err;
33532 },
33533 .@"union" => {
33534 if (!ty.eql(cur_ty)) return generic_err;
33535 },
33536 else => unreachable,
33537 },
33538 else => unreachable,
33539 }
33540 }
33541 return .{ .success = opt_cur_ty.? };
33542 },
33543
33544 .comptime_int => {
33545 for (peer_tys, 0..) |opt_ty, i| {
33546 const ty = opt_ty orelse continue;
33547 switch (ty.zigTypeTag(zcu)) {
33548 .comptime_int => {},
33549 else => return .{ .conflict = .{
33550 .peer_idx_a = strat_reason,
33551 .peer_idx_b = i,
33552 } },
33553 }
33554 }
33555 return .{ .success = .comptime_int };
33556 },
33557
33558 .comptime_float => {
33559 for (peer_tys, 0..) |opt_ty, i| {
33560 const ty = opt_ty orelse continue;
33561 switch (ty.zigTypeTag(zcu)) {
33562 .comptime_int, .comptime_float => {},
33563 else => return .{ .conflict = .{
33564 .peer_idx_a = strat_reason,
33565 .peer_idx_b = i,
33566 } },
33567 }
33568 }
33569 return .{ .success = .comptime_float };
33570 },
33571
33572 .fixed_int => {
33573 var idx_unsigned: ?usize = null;
33574 var idx_signed: ?usize = null;
33575
33576 // TODO: this is for compatibility with legacy behavior. See beneath the loop.
33577 var any_comptime_known = false;
33578
33579 for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| {
33580 const ty = opt_ty orelse continue;
33581 const opt_val = ptr_opt_val.*;
33582
33583 const peer_tag = ty.zigTypeTag(zcu);
33584 switch (peer_tag) {
33585 .comptime_int => {
33586 // If the value is undefined, we can't refine to a fixed-width int
33587 if (opt_val == null or opt_val.?.isUndef(zcu)) return .{ .conflict = .{
33588 .peer_idx_a = strat_reason,
33589 .peer_idx_b = i,
33590 } };
33591 any_comptime_known = true;
33592 ptr_opt_val.* = opt_val.?;
33593 continue;
33594 },
33595 .int => {},
33596 else => return .{ .conflict = .{
33597 .peer_idx_a = strat_reason,
33598 .peer_idx_b = i,
33599 } },
33600 }
33601
33602 if (opt_val != null) any_comptime_known = true;
33603
33604 const info = ty.intInfo(zcu);
33605
33606 const idx_ptr = switch (info.signedness) {
33607 .unsigned => &idx_unsigned,
33608 .signed => &idx_signed,
33609 };
33610
33611 const largest_idx = idx_ptr.* orelse {
33612 idx_ptr.* = i;
33613 continue;
33614 };
33615
33616 const cur_info = peer_tys[largest_idx].?.intInfo(zcu);
33617 if (info.bits > cur_info.bits) {
33618 idx_ptr.* = i;
33619 }
33620 }
33621
33622 if (idx_signed == null) {
33623 return .{ .success = peer_tys[idx_unsigned.?].? };
33624 }
33625
33626 if (idx_unsigned == null) {
33627 return .{ .success = peer_tys[idx_signed.?].? };
33628 }
33629
33630 const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(zcu);
33631 const signed_info = peer_tys[idx_signed.?].?.intInfo(zcu);
33632 if (signed_info.bits > unsigned_info.bits) {
33633 return .{ .success = peer_tys[idx_signed.?].? };
33634 }
33635
33636 // TODO: this is for compatibility with legacy behavior. Before this version of PTR was
33637 // implemented, the algorithm very often returned false positives, with the expectation
33638 // that you'd just hit a coercion error later. One of these was that for integers, the
33639 // largest type would always be returned, even if it couldn't fit everything. This had
33640 // an unintentional consequence to semantics, which is that if values were known at
33641 // comptime, they would be coerced down to the smallest type where possible. This
33642 // behavior is unintuitive and order-dependent, so in my opinion should be eliminated,
33643 // but for now we'll retain compatibility.
33644 if (any_comptime_known) {
33645 if (unsigned_info.bits > signed_info.bits) {
33646 return .{ .success = peer_tys[idx_unsigned.?].? };
33647 }
33648 const idx = @min(idx_unsigned.?, idx_signed.?);
33649 return .{ .success = peer_tys[idx].? };
33650 }
33651
33652 return .{ .conflict = .{
33653 .peer_idx_a = idx_unsigned.?,
33654 .peer_idx_b = idx_signed.?,
33655 } };
33656 },
33657
33658 .fixed_float => {
33659 var opt_cur_ty: ?Type = null;
33660
33661 for (peer_tys, 0..) |opt_ty, i| {
33662 const ty = opt_ty orelse continue;
33663 switch (ty.zigTypeTag(zcu)) {
33664 .comptime_float, .comptime_int, .int => {},
33665 .float => {
33666 if (opt_cur_ty) |cur_ty| {
33667 if (cur_ty.eql(ty)) continue;
33668 // Recreate the type so we eliminate any c_longdouble
33669 const bits = @max(cur_ty.floatBits(target), ty.floatBits(target));
33670 opt_cur_ty = switch (bits) {
33671 16 => .f16,
33672 32 => .f32,
33673 64 => .f64,
33674 80 => .f80,
33675 128 => .f128,
33676 else => unreachable,
33677 };
33678 } else {
33679 opt_cur_ty = ty;
33680 }
33681 },
33682 else => return .{ .conflict = .{
33683 .peer_idx_a = strat_reason,
33684 .peer_idx_b = i,
33685 } },
33686 }
33687 }
33688
33689 // Note that fixed_float is only chosen if there is at least one fixed-width float peer,
33690 // so opt_cur_ty must be non-null.
33691 const cur_ty = opt_cur_ty.?;
33692
33693 // Ensure that any integer peers can coerce safely to the resulting float.
33694 for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| {
33695 const ty = opt_ty orelse continue;
33696 switch (ty.zigTypeTag(zcu)) {
33697 .comptime_float, .comptime_int, .float => {},
33698 .int => {
33699 if (opt_val != null) continue;
33700 const int_info = ty.intInfo(zcu);
33701 const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed);
33702 if (int_precision > cur_ty.floatSignificandBits(target))
33703 return .{ .conflict = .{
33704 .peer_idx_a = strat_reason,
33705 .peer_idx_b = i,
33706 } };
33707 },
33708 else => unreachable, // Previous pass returned on this branch.
33709 }
33710 }
33711
33712 return .{ .success = cur_ty };
33713 },
33714
33715 .tuple => {
33716 // First, check that every peer has the same approximate structure (field count)
33717
33718 var opt_first_idx: ?usize = null;
33719 var is_tuple: bool = undefined;
33720 var field_count: usize = undefined;
33721
33722 for (peer_tys, 0..) |opt_ty, i| {
33723 const ty = opt_ty orelse continue;
33724
33725 if (!ty.isTuple(zcu)) {
33726 return .{ .conflict = .{
33727 .peer_idx_a = strat_reason,
33728 .peer_idx_b = i,
33729 } };
33730 }
33731
33732 const first_idx = opt_first_idx orelse {
33733 opt_first_idx = i;
33734 is_tuple = ty.isTuple(zcu);
33735 field_count = ty.structFieldCount(zcu);
33736 continue;
33737 };
33738
33739 if (ty.structFieldCount(zcu) != field_count) {
33740 return .{ .conflict = .{
33741 .peer_idx_a = first_idx,
33742 .peer_idx_b = i,
33743 } };
33744 }
33745 }
33746
33747 assert(opt_first_idx != null);
33748
33749 // Now, we'll recursively resolve the field types
33750 const field_types = try sema.arena.alloc(InternPool.Index, field_count);
33751 // Values for `comptime` fields - `.none` used for non-comptime fields
33752 const field_vals = try sema.arena.alloc(InternPool.Index, field_count);
33753 const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len);
33754 const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len);
33755
33756 for (field_types, field_vals, 0..) |*field_ty, *field_val, field_index| {
33757 // Fill buffers with types and values of the field
33758 for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| {
33759 const ty = opt_ty orelse {
33760 peer_field_ty.* = null;
33761 peer_field_val.* = null;
33762 continue;
33763 };
33764 peer_field_ty.* = ty.fieldType(field_index, zcu);
33765 peer_field_val.* = if (opt_val) |val| try val.fieldValue(pt, field_index) else null;
33766 }
33767
33768 // Resolve field type recursively
33769 field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) {
33770 .success => |ty| ty.toIntern(),
33771 else => |result| {
33772 const result_buf = try sema.arena.create(PeerResolveResult);
33773 result_buf.* = result;
33774 const field_name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{d}", .{field_index}, .no_embedded_nulls);
33775
33776 // The error info needs the field types, but we can't reuse sub_peer_tys
33777 // since the recursive call may have clobbered it.
33778 const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len);
33779 for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| {
33780 // Already-resolved types won't be referenced by the error so it's fine
33781 // to leave them undefined.
33782 const ty = opt_ty orelse continue;
33783 peer_field_ty.* = ty.fieldType(field_index, zcu);
33784 }
33785
33786 return .{ .field_error = .{
33787 .field_name = field_name,
33788 .field_types = peer_field_tys,
33789 .sub_result = result_buf,
33790 } };
33791 },
33792 };
33793
33794 // Decide if this is a comptime field. If it is comptime in all peers, and the
33795 // coerced comptime values are all the same, we say it is comptime, else not.
33796
33797 var comptime_val: ?Value = null;
33798 for (peer_tys) |opt_ty| {
33799 const struct_ty = opt_ty orelse continue;
33800
33801 const uncoerced_field_val = try struct_ty.structFieldValueComptime(pt, field_index) orelse {
33802 comptime_val = null;
33803 break;
33804 };
33805 const uncoerced_field = Air.internedToRef(uncoerced_field_val.toIntern());
33806 const coerced_inst = sema.coerceExtra(block, .fromInterned(field_ty.*), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) {
33807 // 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
33808 error.NotCoercible => {
33809 comptime_val = null;
33810 break;
33811 },
33812 else => |e| return e,
33813 };
33814 const coerced_val = sema.resolveValue(coerced_inst) orelse continue;
33815 const existing = comptime_val orelse {
33816 comptime_val = coerced_val;
33817 continue;
33818 };
33819 if (!coerced_val.eql(existing, .fromInterned(field_ty.*), zcu)) {
33820 comptime_val = null;
33821 break;
33822 }
33823 }
33824
33825 field_val.* = if (comptime_val) |v| v.toIntern() else .none;
33826 }
33827
33828 const final_ty = try ip.getTupleType(gpa, io, pt.tid, .{
33829 .types = field_types,
33830 .values = field_vals,
33831 });
33832
33833 return .{ .success = .fromInterned(final_ty) };
33834 },
33835
33836 .exact => {
33837 var expect_ty: ?Type = null;
33838 var first_idx: usize = undefined;
33839 for (peer_tys, 0..) |opt_ty, i| {
33840 const ty = opt_ty orelse continue;
33841 if (expect_ty) |expect| {
33842 if (!ty.eql(expect)) return .{ .conflict = .{
33843 .peer_idx_a = first_idx,
33844 .peer_idx_b = i,
33845 } };
33846 } else {
33847 expect_ty = ty;
33848 first_idx = i;
33849 }
33850 }
33851 return .{ .success = expect_ty.? };
33852 },
33853 }
33854}
33855
33856fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type {
33857 // e0 -> e1
33858 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) {
33859 return e1;
33860 }
33861
33862 // e1 -> e0
33863 if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) {
33864 return e0;
33865 }
33866
33867 return sema.errorSetMerge(e0, e1);
33868}
33869
33870fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type {
33871 const target = sema.pt.zcu.getTarget();
33872
33873 // ty_b -> ty_a
33874 if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, false, target, src, src, null)) {
33875 return ty_a;
33876 }
33877
33878 // ty_a -> ty_b
33879 if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, false, target, src, src, null)) {
33880 return ty_b;
33881 }
33882
33883 return null;
33884}
33885
33886const ArrayLike = struct {
33887 len: u64,
33888 /// `noreturn` indicates that this type is `struct{}` so can coerce to anything
33889 elem_ty: Type,
33890};
33891fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike {
33892 const pt = sema.pt;
33893 const zcu = pt.zcu;
33894 return switch (ty.zigTypeTag(zcu)) {
33895 .array => .{
33896 .len = ty.arrayLen(zcu),
33897 .elem_ty = ty.childType(zcu),
33898 },
33899 .@"struct" => {
33900 if (!ty.isTuple(zcu)) return null;
33901 const field_count = ty.structFieldCount(zcu);
33902 if (field_count == 0) return .{
33903 .len = 0,
33904 .elem_ty = .noreturn,
33905 };
33906 const elem_ty = ty.fieldType(0, zcu);
33907 for (1..field_count) |i| {
33908 if (!ty.fieldType(i, zcu).eql(elem_ty)) {
33909 return null;
33910 }
33911 }
33912 return .{
33913 .len = field_count,
33914 .elem_ty = elem_ty,
33915 };
33916 },
33917 else => null,
33918 };
33919}
33920
33921fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
33922 const pt = sema.pt;
33923 if (!ty.isIndexable(pt.zcu)) {
33924 const msg = msg: {
33925 const msg = try sema.errMsg(src, "type '{f}' does not support indexing", .{ty.fmt(pt)});
33926 errdefer msg.destroy(sema.gpa);
33927 try sema.errNote(src, msg, "operand must be an array, slice, tuple, or vector", .{});
33928 try sema.addDeclaredHereNote(msg, ty);
33929 break :msg msg;
33930 };
33931 return sema.failWithOwnedErrorMsg(block, msg);
33932 }
33933}
33934
33935fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void {
33936 const pt = sema.pt;
33937 const zcu = pt.zcu;
33938 if (ty.zigTypeTag(zcu) == .pointer) {
33939 switch (ty.ptrSize(zcu)) {
33940 .slice, .many, .c => return,
33941 .one => {
33942 const elem_ty = ty.childType(zcu);
33943 if (elem_ty.zigTypeTag(zcu) == .array) return;
33944 // TODO https://github.com/ziglang/zig/issues/15479
33945 // if (elem_ty.isTuple()) return;
33946 },
33947 }
33948 }
33949 const msg = msg: {
33950 const msg = try sema.errMsg(src, "type '{f}' is not an indexable pointer", .{ty.fmt(pt)});
33951 errdefer msg.destroy(sema.gpa);
33952 try sema.errNote(src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{});
33953 break :msg msg;
33954 };
33955 return sema.failWithOwnedErrorMsg(block, msg);
33956}
33957
33958/// Resolves the inferred error set of the given function, so that the corresponding concrete error
33959/// set is available by calling `InternPool.funcIesResolvedUnordered` on `func_index`.
33960///
33961/// Asserts that `func_index` is a function. Also asserts that it is not a coerced function, because
33962/// coerced functions do not own inferred error sets.
33963fn ensureFuncIesResolved(
33964 sema: *Sema,
33965 block: *Block,
33966 src: LazySrcLoc,
33967 func_index: InternPool.Index,
33968) CompileError!void {
33969 const pt = sema.pt;
33970 const zcu = pt.zcu;
33971 const ip = &zcu.intern_pool;
33972
33973 assert(ip.unwrapCoercedFunc(func_index) == func_index);
33974
33975 try sema.declareDependency(.{ .func_ies = func_index });
33976 try sema.addReferenceEntry(block, src, .wrap(.{ .func = func_index }));
33977
33978 const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined };
33979
33980 if (zcu.analysis_in_progress.contains(.wrap(.{ .func = func_index }))) {
33981 return sema.failWithDependencyLoop(.wrap(.{ .func = func_index }), &reason);
33982 }
33983
33984 pt.ensureFuncBodyUpToDate(func_index, &reason) catch |err| switch (err) {
33985 error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = .wrap(.{ .func = func_index }) }),
33986 else => |e| return e,
33987 };
33988}
33989
33990pub fn resolveInferredErrorSetPtr(
33991 sema: *Sema,
33992 block: *Block,
33993 src: LazySrcLoc,
33994 ies: *InferredErrorSet,
33995) CompileError!void {
33996 const pt = sema.pt;
33997 const ip = &pt.zcu.intern_pool;
33998
33999 if (ies.resolved != .none) return;
34000
34001 const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern());
34002
34003 for (ies.inferred_error_sets.keys()) |other_ies_index| {
34004 if (ies_index == other_ies_index) continue;
34005 const other_func_index = ip.iesFuncIndex(other_ies_index);
34006 try sema.ensureFuncIesResolved(block, src, other_func_index);
34007 switch (ip.funcIesResolvedUnordered(other_func_index)) {
34008 .anyerror_type => {
34009 ies.resolved = .anyerror_type;
34010 return;
34011 },
34012 else => |error_set_ty_index| {
34013 const names = ip.indexToKey(error_set_ty_index).error_set_type.names;
34014 for (names.get(ip)) |name| {
34015 try ies.errors.put(sema.arena, name, {});
34016 }
34017 },
34018 }
34019 }
34020
34021 const resolved_error_set_ty = try pt.errorSetFromUnsortedNames(ies.errors.keys());
34022 ies.resolved = resolved_error_set_ty.toIntern();
34023}
34024
34025fn resolveAdHocInferredErrorSet(
34026 sema: *Sema,
34027 block: *Block,
34028 src: LazySrcLoc,
34029 value: InternPool.Index,
34030) CompileError!InternPool.Index {
34031 const pt = sema.pt;
34032 const zcu = pt.zcu;
34033 const comp = zcu.comp;
34034 const gpa = comp.gpa;
34035 const io = comp.io;
34036 const ip = &zcu.intern_pool;
34037
34038 const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value));
34039 if (new_ty == .none) return value;
34040 return ip.getCoerced(gpa, io, pt.tid, value, new_ty);
34041}
34042
34043fn resolveAdHocInferredErrorSetTy(
34044 sema: *Sema,
34045 block: *Block,
34046 src: LazySrcLoc,
34047 ty: InternPool.Index,
34048) CompileError!InternPool.Index {
34049 const ies = sema.fn_ret_ty_ies orelse return .none;
34050 const pt = sema.pt;
34051 const zcu = pt.zcu;
34052 const ip = &zcu.intern_pool;
34053 const error_union_info = switch (ip.indexToKey(ty)) {
34054 .error_union_type => |x| x,
34055 else => return .none,
34056 };
34057 if (error_union_info.error_set_type != .adhoc_inferred_error_set_type)
34058 return .none;
34059
34060 try sema.resolveInferredErrorSetPtr(block, src, ies);
34061 const new_ty = try pt.intern(.{ .error_union_type = .{
34062 .error_set_type = ies.resolved,
34063 .payload_type = error_union_info.payload_type,
34064 } });
34065 return new_ty;
34066}
34067
34068fn resolveInferredErrorSetTy(
34069 sema: *Sema,
34070 block: *Block,
34071 src: LazySrcLoc,
34072 ty: InternPool.Index,
34073) CompileError!InternPool.Index {
34074 const pt = sema.pt;
34075 const zcu = pt.zcu;
34076 const ip = &zcu.intern_pool;
34077 if (ty == .anyerror_type) return ty;
34078 switch (ip.indexToKey(ty)) {
34079 .error_set_type => return ty,
34080 .inferred_error_set_type => |func_index| {
34081 try sema.ensureFuncIesResolved(block, src, func_index);
34082 return ip.funcIesResolvedUnordered(func_index);
34083 },
34084 else => unreachable,
34085 }
34086}
34087
34088/// Returns the type of the AIR instruction.
34089fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type {
34090 return sema.getTmpAir().typeOf(inst, &sema.pt.zcu.intern_pool);
34091}
34092
34093pub fn getTmpAir(sema: Sema) Air {
34094 return .{
34095 .instructions = sema.air_instructions.slice(),
34096 .extra = sema.air_extra,
34097 };
34098}
34099
34100pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 {
34101 const field_count = @typeInfo(@TypeOf(extra)).@"struct".field_names.len;
34102 try sema.air_extra.ensureUnusedCapacity(sema.gpa, field_count);
34103 return sema.addExtraAssumeCapacity(extra);
34104}
34105
34106pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 {
34107 const result: u32 = @intCast(sema.air_extra.items.len);
34108 sema.air_extra.appendSliceAssumeCapacity(&payloadToExtraItems(extra));
34109 return result;
34110}
34111
34112fn payloadToExtraItems(data: anytype) [@typeInfo(@TypeOf(data)).@"struct".field_names.len]u32 {
34113 const info = @typeInfo(@TypeOf(data)).@"struct";
34114 var result: [info.field_names.len]u32 = undefined;
34115 inline for (&result, info.field_names, info.field_types) |*val, field_name, field_type| {
34116 val.* = switch (field_type) {
34117 u32 => @field(data, field_name),
34118 i32, Air.CondBr.BranchHints, Air.Asm.Flags => @bitCast(@field(data, field_name)),
34119 Air.Inst.Ref, InternPool.Index => @backingInt(@field(data, field_name)),
34120 else => @compileError("bad field type: " ++ @typeName(field_type)),
34121 };
34122 }
34123 return result;
34124}
34125
34126fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void {
34127 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(refs));
34128}
34129
34130fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index {
34131 const air_datas = sema.air_instructions.items(.data);
34132 const air_tags = sema.air_instructions.items(.tag);
34133 switch (air_tags[@backingInt(inst_index)]) {
34134 .br => return air_datas[@backingInt(inst_index)].br.block_inst,
34135 else => return null,
34136 }
34137}
34138
34139fn isComptimeKnown(
34140 sema: *Sema,
34141 inst: Air.Inst.Ref,
34142) !bool {
34143 return sema.resolveValue(inst) != null;
34144}
34145
34146/// Asserts that the layout of `var_type` has already been resolved.
34147fn analyzeComptimeAlloc(
34148 sema: *Sema,
34149 block: *Block,
34150 src: LazySrcLoc,
34151 var_type: Type,
34152 alignment: Alignment,
34153) CompileError!Air.Inst.Ref {
34154 const pt = sema.pt;
34155 const zcu = pt.zcu;
34156
34157 var_type.assertHasLayout(zcu);
34158
34159 const ptr_type = try pt.ptrType(.{
34160 .child = var_type.toIntern(),
34161 .flags = .{
34162 .alignment = alignment,
34163 .address_space = target_util.defaultAddressSpace(zcu.getTarget(), .global_constant),
34164 },
34165 });
34166
34167 if (try var_type.onePossibleValue(pt)) |opv| {
34168 return .fromIntern(try pt.intern(.{ .ptr = .{
34169 .ty = ptr_type.toIntern(),
34170 .base_addr = .{ .uav = .{
34171 .val = opv.toIntern(),
34172 .orig_ty = ptr_type.toIntern(),
34173 } },
34174 .byte_offset = 0,
34175 } }));
34176 } else {
34177 const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment);
34178 return .fromIntern(try pt.intern(.{ .ptr = .{
34179 .ty = ptr_type.toIntern(),
34180 .base_addr = .{ .comptime_alloc = alloc },
34181 .byte_offset = 0,
34182 } }));
34183 }
34184}
34185
34186fn resolveAddressSpace(
34187 sema: *Sema,
34188 block: *Block,
34189 src: LazySrcLoc,
34190 zir_ref: Zir.Inst.Ref,
34191 ctx: std.Target.AddressSpaceContext,
34192) !std.lang.AddressSpace {
34193 const air_ref = sema.resolveInst(zir_ref);
34194 return sema.analyzeAsAddressSpace(block, src, air_ref, ctx);
34195}
34196
34197pub fn analyzeAsAddressSpace(
34198 sema: *Sema,
34199 block: *Block,
34200 src: LazySrcLoc,
34201 air_ref: Air.Inst.Ref,
34202 ctx: std.Target.AddressSpaceContext,
34203) !std.lang.AddressSpace {
34204 const pt = sema.pt;
34205 const addrspace_ty = try sema.getStdLangType(src, .AddressSpace);
34206 const coerced = try sema.coerce(block, addrspace_ty, air_ref, src);
34207 const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{ .simple = .@"addrspace" });
34208 const address_space = try sema.interpretStdLangType(block, src, addrspace_val, std.lang.AddressSpace);
34209 const target = pt.zcu.getTarget();
34210
34211 if (!target.supportsAddressSpace(address_space, ctx)) {
34212 // TODO error messages could be made more elaborate here
34213 const entity = switch (ctx) {
34214 .function => "functions",
34215 .variable => "mutable values",
34216 .constant => "constant values",
34217 .pointer => "pointers",
34218 };
34219 return sema.fail(
34220 block,
34221 src,
34222 "{s} with address space '{s}' are not supported on {s}",
34223 .{ entity, @tagName(address_space), @tagName(target.cpu.arch.family()) },
34224 );
34225 }
34226
34227 return address_space;
34228}
34229
34230/// Asserts the value is a pointer and dereferences it.
34231/// Returns `null` if the pointer contents cannot be loaded at comptime.
34232fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
34233 // TODO: audit use sites to eliminate this coercion
34234 const pt = sema.pt;
34235 const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty);
34236 switch (try sema.pointerDerefExtra(block, src, coerced_ptr_val)) {
34237 .runtime_load => return null,
34238 .val => |v| return v,
34239 .needed_well_defined => |ty| return sema.fail(
34240 block,
34241 src,
34242 "comptime dereference requires '{f}' to have a well-defined layout",
34243 .{ty.fmt(pt)},
34244 ),
34245 .out_of_bounds => |ty| return sema.fail(
34246 block,
34247 src,
34248 "dereference of '{f}' exceeds bounds of containing decl of type '{f}'",
34249 .{ ptr_ty.fmt(pt), ty.fmt(pt) },
34250 ),
34251 }
34252}
34253
34254const DerefResult = union(enum) {
34255 runtime_load,
34256 val: Value,
34257 needed_well_defined: Type,
34258 out_of_bounds: Type,
34259};
34260
34261fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value) CompileError!DerefResult {
34262 const pt = sema.pt;
34263 const ip = &pt.zcu.intern_pool;
34264 switch (try sema.loadComptimePtr(block, src, ptr_val)) {
34265 .success => |mv| return .{ .val = try mv.intern(pt, sema.arena) },
34266 .runtime_load => return .runtime_load,
34267 .undef => return sema.failWithUseOfUndef(block, src, null),
34268 .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}),
34269 .null_payload => return sema.fail(block, src, "attempt to use null value", .{}),
34270 .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}),
34271 .needed_well_defined => |ty| return .{ .needed_well_defined = ty },
34272 .out_of_bounds => |ty| return .{ .out_of_bounds = ty },
34273 .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}),
34274 }
34275}
34276
34277/// Used to convert a u64 value to a usize value, emitting a compile error if the number
34278/// is too big to fit.
34279fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize {
34280 if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int;
34281 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});
34282}
34283
34284/// Asserts that the layout of `union_ty` is already resolved.
34285fn unionFieldIndex(
34286 sema: *Sema,
34287 block: *Block,
34288 union_ty: Type,
34289 field_name: InternPool.NullTerminatedString,
34290 field_src: LazySrcLoc,
34291) !u32 {
34292 const pt = sema.pt;
34293 const zcu = pt.zcu;
34294 const ip = &zcu.intern_pool;
34295 const union_obj = zcu.typeToUnion(union_ty).?;
34296 const enum_obj = ip.loadEnumType(union_obj.enum_tag_type);
34297 const field_index = enum_obj.nameIndex(ip, field_name) orelse
34298 return sema.failWithBadUnionFieldAccess(block, union_ty, union_obj, field_src, field_name);
34299 return @intCast(field_index);
34300}
34301
34302/// Asserts that the layout of `struct_ty` is already resolved.
34303fn structFieldIndex(
34304 sema: *Sema,
34305 block: *Block,
34306 struct_ty: Type,
34307 field_name: InternPool.NullTerminatedString,
34308 field_src: LazySrcLoc,
34309) !u32 {
34310 const pt = sema.pt;
34311 const zcu = pt.zcu;
34312 const ip = &zcu.intern_pool;
34313 const struct_type = zcu.typeToStruct(struct_ty).?;
34314 return struct_type.nameIndex(ip, field_name) orelse
34315 return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name);
34316}
34317
34318const IntFromFloatMode = enum { exact, truncate, round, floor, ceil };
34319
34320fn intFromFloat(
34321 sema: *Sema,
34322 block: *Block,
34323 src: LazySrcLoc,
34324 val: Value,
34325 float_ty: Type,
34326 int_ty: Type,
34327 mode: IntFromFloatMode,
34328) CompileError!Value {
34329 const pt = sema.pt;
34330 const zcu = pt.zcu;
34331 if (float_ty.zigTypeTag(zcu) == .vector) {
34332 const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(zcu));
34333 for (result_data, 0..) |*scalar, elem_idx| {
34334 const elem_val = try val.elemValue(pt, elem_idx);
34335 scalar.* = (try sema.intFromFloatScalar(block, src, elem_val, int_ty.scalarType(zcu), mode, elem_idx)).toIntern();
34336 }
34337 return pt.aggregateValue(int_ty, result_data);
34338 }
34339 return sema.intFromFloatScalar(block, src, val, int_ty, mode, null);
34340}
34341
34342fn intFromFloatScalar(
34343 sema: *Sema,
34344 block: *Block,
34345 src: LazySrcLoc,
34346 val: Value,
34347 int_ty: Type,
34348 mode: IntFromFloatMode,
34349 vec_idx: ?usize,
34350) CompileError!Value {
34351 const pt = sema.pt;
34352 const zcu = pt.zcu;
34353
34354 if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, vec_idx);
34355
34356 var float = val.toFloat(f128, zcu);
34357 switch (mode) {
34358 .round => float = @round(float),
34359 .floor => float = @floor(float),
34360 .ceil => float = @ceil(float),
34361 .truncate, .exact => {},
34362 }
34363
34364 if (std.math.isNan(float)) {
34365 return sema.fail(block, src, "float value NaN cannot be stored in integer type '{f}'", .{
34366 int_ty.fmt(pt),
34367 });
34368 }
34369 if (std.math.isInf(float)) {
34370 return sema.fail(block, src, "float value Inf cannot be stored in integer type '{f}'", .{
34371 int_ty.fmt(pt),
34372 });
34373 }
34374
34375 var big_int: std.math.big.int.Mutable = .{
34376 .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)),
34377 .len = undefined,
34378 .positive = undefined,
34379 };
34380 switch (big_int.setFloat(float, .trunc)) {
34381 .inexact => switch (mode) {
34382 .exact => return sema.fail(
34383 block,
34384 src,
34385 "fractional component prevents float value '{f}' from coercion to type '{f}'",
34386 .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) },
34387 ),
34388 .truncate, .round, .floor, .ceil => {},
34389 },
34390 .exact => {},
34391 }
34392 const cti_result = try pt.intValue_big(.comptime_int, big_int.toConst());
34393 if (int_ty.toIntern() == .comptime_int_type) return cti_result;
34394
34395 const int_info = int_ty.intInfo(zcu);
34396 if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) {
34397 return sema.fail(block, src, "float value '{f}' cannot be stored in integer type '{f}'", .{
34398 val.fmtValueSema(pt, sema), int_ty.fmt(pt),
34399 });
34400 }
34401 return pt.getCoerced(cti_result, int_ty);
34402}
34403
34404/// Asserts the type is an exhaustive enum.
34405fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool {
34406 const pt = sema.pt;
34407 const zcu = pt.zcu;
34408 const enum_type = zcu.intern_pool.loadEnumType(ty.toIntern());
34409 assert(!enum_type.nonexhaustive);
34410 // The `tagValueIndex` function call below relies on the type being the integer tag type.
34411 // `getCoerced` assumes the value will fit the new type.
34412 const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type);
34413 if (int_tag_ty.classify(zcu) == .no_possible_value) return false;
34414 if (!int.intFitsInType(int_tag_ty, null, zcu)) return false;
34415 const int_coerced = try pt.getCoerced(int, int_tag_ty);
34416 return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null;
34417}
34418
34419/// Asserts the values are comparable. Both operands have type `ty`.
34420/// For vectors, returns true if the comparison is true for ALL elements.
34421///
34422/// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)`
34423fn compareAll(
34424 sema: *Sema,
34425 lhs: Value,
34426 op: std.math.CompareOperator,
34427 rhs: Value,
34428 ty: Type,
34429) CompileError!bool {
34430 const pt = sema.pt;
34431 const zcu = pt.zcu;
34432 if (ty.zigTypeTag(zcu) == .vector) {
34433 var i: usize = 0;
34434 while (i < ty.vectorLen(zcu)) : (i += 1) {
34435 const lhs_elem = try lhs.elemValue(pt, i);
34436 const rhs_elem = try rhs.elemValue(pt, i);
34437 if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu)))) {
34438 return false;
34439 }
34440 }
34441 return true;
34442 }
34443 return sema.compareScalar(lhs, op, rhs, ty);
34444}
34445
34446/// Asserts the values are comparable. Both operands have type `ty`.
34447fn compareScalar(
34448 sema: *Sema,
34449 lhs: Value,
34450 op: std.math.CompareOperator,
34451 rhs: Value,
34452 ty: Type,
34453) CompileError!bool {
34454 const pt = sema.pt;
34455 const zcu = pt.zcu;
34456
34457 const coerced_lhs = try pt.getCoerced(lhs, ty);
34458 const coerced_rhs = try pt.getCoerced(rhs, ty);
34459
34460 // Equality comparisons of signed zero and NaN need to use floating point semantics
34461 if (coerced_lhs.isFloat(zcu) or coerced_rhs.isFloat(zcu))
34462 return Value.compareHetero(coerced_lhs, op, coerced_rhs, zcu);
34463
34464 switch (op) {
34465 .eq => return Value.eql(coerced_lhs, coerced_rhs, ty, zcu),
34466 .neq => return !Value.eql(coerced_lhs, coerced_rhs, ty, zcu),
34467 else => return Value.compareHetero(coerced_lhs, op, coerced_rhs, zcu),
34468 }
34469}
34470
34471fn valuesEqual(
34472 sema: *Sema,
34473 lhs: Value,
34474 rhs: Value,
34475 ty: Type,
34476) CompileError!bool {
34477 return lhs.eql(rhs, ty, sema.pt.zcu);
34478}
34479
34480/// Asserts the values are comparable vectors of type `ty`.
34481fn compareVector(
34482 sema: *Sema,
34483 lhs: Value,
34484 op: std.math.CompareOperator,
34485 rhs: Value,
34486 ty: Type,
34487) !Value {
34488 const pt = sema.pt;
34489 const zcu = pt.zcu;
34490 assert(ty.zigTypeTag(zcu) == .vector);
34491 const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(zcu));
34492 for (result_data, 0..) |*scalar, i| {
34493 const lhs_elem = try lhs.elemValue(pt, i);
34494 const rhs_elem = try rhs.elemValue(pt, i);
34495 if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) {
34496 scalar.* = .undef_bool;
34497 } else {
34498 const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu));
34499 scalar.* = Value.makeBool(res_bool).toIntern();
34500 }
34501 }
34502 return pt.aggregateValue(try pt.vectorType(.{
34503 .len = ty.vectorLen(zcu),
34504 .child = .bool_type,
34505 }), result_data);
34506}
34507
34508/// Merge lhs with rhs.
34509/// Asserts that lhs and rhs are both error sets and are resolved.
34510fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type {
34511 const pt = sema.pt;
34512 const ip = &pt.zcu.intern_pool;
34513 const arena = sema.arena;
34514 const lhs_names = lhs.errorSetNames(pt.zcu);
34515 const rhs_names = rhs.errorSetNames(pt.zcu);
34516 var names: InferredErrorSet.NameMap = .{};
34517 try names.ensureUnusedCapacity(arena, lhs_names.len);
34518
34519 for (0..lhs_names.len) |lhs_index| {
34520 names.putAssumeCapacityNoClobber(lhs_names.get(ip)[lhs_index], {});
34521 }
34522 for (0..rhs_names.len) |rhs_index| {
34523 try names.put(arena, rhs_names.get(ip)[rhs_index], {});
34524 }
34525
34526 return pt.errorSetFromUnsortedNames(names.keys());
34527}
34528
34529pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
34530 const pt = sema.pt;
34531 if (!pt.zcu.comp.config.incremental) return;
34532
34533 const gop = try sema.dependencies.getOrPut(sema.gpa, dependee);
34534 if (gop.found_existing) return;
34535
34536 try pt.addDependency(sema.owner, dependee);
34537}
34538
34539fn isComptimeMutablePtr(sema: *Sema, val: Value) bool {
34540 return switch (sema.pt.zcu.intern_pool.indexToKey(val.toIntern())) {
34541 .slice => |slice| sema.isComptimeMutablePtr(Value.fromInterned(slice.ptr)),
34542 .ptr => |ptr| switch (ptr.base_addr) {
34543 .uav, .nav, .int => false,
34544 .comptime_field => true,
34545 .comptime_alloc => |alloc_index| !sema.getComptimeAlloc(alloc_index).is_const,
34546 .eu_payload, .opt_payload => |base| sema.isComptimeMutablePtr(Value.fromInterned(base)),
34547 .arr_elem, .field => |bi| sema.isComptimeMutablePtr(Value.fromInterned(bi.base)),
34548 },
34549 else => false,
34550 };
34551}
34552
34553fn checkRuntimeValue(sema: *Sema, ptr: Air.Inst.Ref) bool {
34554 const val = ptr.toInterned() orelse return true;
34555 return !Value.fromInterned(val).canMutateComptimeVarState(sema.pt.zcu);
34556}
34557
34558fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Air.Inst.Ref) CompileError!void {
34559 if (sema.checkRuntimeValue(val)) return;
34560 return sema.failWithOwnedErrorMsg(block, msg: {
34561 const pt = sema.pt;
34562 const zcu = pt.zcu;
34563 const comp = zcu.comp;
34564 const gpa = comp.gpa;
34565 const io = comp.io;
34566
34567 const msg = try sema.errMsg(val_src, "runtime value contains reference to comptime var", .{});
34568 errdefer msg.destroy(gpa);
34569 try sema.errNote(val_src, msg, "comptime var pointers are not available at runtime", .{});
34570 const val_str = try pt.zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "runtime_value", .no_embedded_nulls);
34571 try sema.explainWhyValueContainsReferenceToComptimeVar(msg, val_src, val_str, .fromInterned(val.toInterned().?));
34572 break :msg msg;
34573 });
34574}
34575
34576pub fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError {
34577 return sema.failWithOwnedErrorMsg(block, msg: {
34578 const msg = try sema.errMsg(src, "{s} contains reference to comptime var", .{kind_of_value});
34579 errdefer msg.destroy(sema.gpa);
34580 if (val) |v| try sema.explainWhyValueContainsReferenceToComptimeVar(msg, src, value_name, v);
34581 break :msg msg;
34582 });
34583}
34584
34585fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, val: Value) Allocator.Error!void {
34586 // Our goal is something like this:
34587 // note: '(value.? catch unreachable)[0]' points to 'v0.?.foo'
34588 // note: '(v0.?.bar catch unreachable)' points to 'v1'
34589 // note: 'v1.?' points to a comptime var
34590
34591 var intermediate_value_count: u32 = 0;
34592 var cur_val: Value = val;
34593 while (true) {
34594 switch (try sema.notePathToComptimeAllocPtr(msg, src, cur_val, intermediate_value_count, value_name)) {
34595 .done => return,
34596 .new_val => |new_val| {
34597 intermediate_value_count += 1;
34598 cur_val = new_val;
34599 },
34600 }
34601 }
34602}
34603
34604fn notePathToComptimeAllocPtr(
34605 sema: *Sema,
34606 msg: *Zcu.ErrorMsg,
34607 src: LazySrcLoc,
34608 val: Value,
34609 intermediate_value_count: u32,
34610 start_value_name: InternPool.NullTerminatedString,
34611) Allocator.Error!union(enum) {
34612 done,
34613 new_val: Value,
34614} {
34615 const arena = sema.arena;
34616 const pt = sema.pt;
34617 const zcu = pt.zcu;
34618 const ip = &zcu.intern_pool;
34619
34620 var first_path: std.ArrayList(u8) = .empty;
34621 if (intermediate_value_count == 0) {
34622 try first_path.print(arena, "{f}", .{start_value_name.fmt(ip)});
34623 } else {
34624 try first_path.print(arena, "v{d}", .{intermediate_value_count - 1});
34625 }
34626
34627 const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path);
34628
34629 switch (ip.indexToKey(comptime_ptr.toIntern()).ptr.base_addr) {
34630 .comptime_field => {
34631 try sema.errNote(src, msg, "'{s}' points to comptime field", .{first_path.items});
34632 return .done;
34633 },
34634 .comptime_alloc => |idx| {
34635 const cta = sema.getComptimeAlloc(idx);
34636 if (!cta.is_const) {
34637 try sema.errNote(cta.src, msg, "'{s}' points to comptime var declared here", .{first_path.items});
34638 return .done;
34639 }
34640 },
34641 else => {}, // there will be another stage
34642 }
34643
34644 const derivation = try comptime_ptr.pointerDerivation(arena, pt, sema);
34645
34646 var second_path_aw: std.Io.Writer.Allocating = .init(arena);
34647 defer second_path_aw.deinit();
34648 const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count});
34649 const deriv_start = @import("print_value.zig").printPtrDerivation(
34650 derivation,
34651 &second_path_aw.writer,
34652 pt,
34653 .lvalue,
34654 .{ .str = inter_name },
34655 20,
34656 ) catch return error.OutOfMemory;
34657
34658 switch (deriv_start) {
34659 .int, .nav_ptr => unreachable,
34660 .uav_ptr => |uav| {
34661 try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() });
34662 return .{ .new_val = .fromInterned(uav.val) };
34663 },
34664 .comptime_alloc_ptr => |cta_info| {
34665 try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() });
34666 const cta = sema.getComptimeAlloc(cta_info.idx);
34667 if (cta.is_const) {
34668 return .{ .new_val = cta_info.val };
34669 } else {
34670 try sema.errNote(cta.src, msg, "'{s}' is a comptime var declared here", .{inter_name});
34671 return .done;
34672 }
34673 },
34674 .comptime_field_ptr => {
34675 try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() });
34676 try sema.errNote(src, msg, "'{s}' is a comptime field", .{inter_name});
34677 return .done;
34678 },
34679 .eu_payload_ptr,
34680 .opt_payload_ptr,
34681 .field_ptr,
34682 .elem_ptr,
34683 .offset_and_cast,
34684 => unreachable,
34685 }
34686}
34687
34688fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList(u8)) Allocator.Error!Value {
34689 const pt = sema.pt;
34690 const zcu = pt.zcu;
34691 const ip = &zcu.intern_pool;
34692 const arena = sema.arena;
34693 assert(val.canMutateComptimeVarState(zcu));
34694 switch (ip.indexToKey(val.toIntern())) {
34695 .ptr => return val,
34696 .error_union => |eu| {
34697 try path.insert(arena, 0, '(');
34698 try path.appendSlice(arena, " catch unreachable)");
34699 return sema.notePathToComptimeAllocPtrInner(.fromInterned(eu.val.payload), path);
34700 },
34701 .slice => |slice| {
34702 try path.appendSlice(arena, ".ptr");
34703 return sema.notePathToComptimeAllocPtrInner(.fromInterned(slice.ptr), path);
34704 },
34705 .opt => |opt| {
34706 try path.appendSlice(arena, ".?");
34707 return sema.notePathToComptimeAllocPtrInner(.fromInterned(opt.val), path);
34708 },
34709 .un => |un| {
34710 assert(un.tag != .none);
34711 const union_ty: Type = .fromInterned(un.ty);
34712 const backing_enum = union_ty.unionTagTypeHypothetical(zcu);
34713 const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?;
34714 const field_name = backing_enum.enumFieldName(field_idx, zcu);
34715 try path.print(arena, ".{f}", .{field_name.fmt(ip)});
34716 return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path);
34717 },
34718 .aggregate => |agg| {
34719 const elem: InternPool.Index, const elem_idx: usize = switch (agg.storage) {
34720 .bytes => unreachable,
34721 .repeated_elem => |elem| .{ elem, 0 },
34722 .elems => |elems| for (elems, 0..) |elem, elem_idx| {
34723 if (Value.fromInterned(elem).canMutateComptimeVarState(zcu)) {
34724 break .{ elem, elem_idx };
34725 }
34726 } else unreachable,
34727 };
34728 const agg_ty: Type = .fromInterned(agg.ty);
34729 switch (agg_ty.zigTypeTag(zcu)) {
34730 .array, .vector => try path.print(arena, "[{d}]", .{elem_idx}),
34731 .pointer => switch (elem_idx) {
34732 Value.slice_ptr_index => try path.appendSlice(arena, ".ptr"),
34733 Value.slice_len_index => try path.appendSlice(arena, ".len"),
34734 else => unreachable,
34735 },
34736 .@"struct" => if (agg_ty.isTuple(zcu)) {
34737 try path.print(arena, "[{d}]", .{elem_idx});
34738 } else {
34739 const name = agg_ty.structFieldName(elem_idx, zcu).unwrap().?;
34740 try path.print(arena, ".{f}", .{name.fmt(ip)});
34741 },
34742 else => unreachable,
34743 }
34744 return sema.notePathToComptimeAllocPtrInner(.fromInterned(elem), path);
34745 },
34746 else => unreachable,
34747 }
34748}
34749
34750/// Returns true if any value contained in `val` is undefined.
34751fn anyUndef(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) !bool {
34752 const pt = sema.pt;
34753 const zcu = pt.zcu;
34754 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
34755 .undef => true,
34756 .slice => {
34757 // If the slice contents are runtime-known, reification will fail later on with a
34758 // specific error message.
34759 const arr = try sema.maybeDerefSliceAsArray(block, src, val) orelse return false;
34760 return sema.anyUndef(block, src, arr);
34761 },
34762 .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| {
34763 const elem = zcu.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i];
34764 if (try sema.anyUndef(block, src, Value.fromInterned(elem))) break true;
34765 } else false,
34766 else => false,
34767 };
34768}
34769
34770/// Asserts that `slice_val` is a slice of `u8`.
34771fn sliceToIpString(
34772 sema: *Sema,
34773 block: *Block,
34774 src: LazySrcLoc,
34775 slice_val: Value,
34776 reason: ComptimeReason,
34777) CompileError!InternPool.NullTerminatedString {
34778 const pt = sema.pt;
34779 const zcu = pt.zcu;
34780 const slice_ty = slice_val.typeOf(zcu);
34781 assert(slice_ty.isSlice(zcu));
34782 assert(slice_ty.childType(zcu).toIntern() == .u8_type);
34783 const array_val = try sema.derefSliceAsArray(block, src, slice_val, reason);
34784 const array_ty = array_val.typeOf(zcu);
34785 return array_val.toIpString(array_ty, pt);
34786}
34787
34788/// Given a slice value, attempts to dereference it into a comptime-known array.
34789/// Emits a compile error if the contents of the slice are not comptime-known.
34790/// Asserts that `slice_val` is a slice or a pointer to an array.
34791fn derefSliceAsArray(
34792 sema: *Sema,
34793 block: *Block,
34794 src: LazySrcLoc,
34795 slice_val: Value,
34796 /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value
34797 /// being comptime-resolved is that the block is being comptime-evaluated.
34798 reason: ?ComptimeReason,
34799) CompileError!Value {
34800 return try sema.maybeDerefSliceAsArray(block, src, slice_val) orelse {
34801 return sema.failWithNeededComptime(block, src, reason);
34802 };
34803}
34804
34805/// Given a slice value, attempts to dereference it into a comptime-known array.
34806/// Returns `null` if the contents of the slice are not comptime-known.
34807/// Asserts that `slice_val` is a slice or a pointer to an array.
34808fn maybeDerefSliceAsArray(
34809 sema: *Sema,
34810 block: *Block,
34811 src: LazySrcLoc,
34812 slice_val: Value,
34813) CompileError!?Value {
34814 const pt = sema.pt;
34815 const zcu = pt.zcu;
34816 const slice_ty = slice_val.typeOf(zcu);
34817 assert(slice_ty.zigTypeTag(zcu) == .pointer);
34818 switch (slice_ty.ptrInfo(zcu).flags.size) {
34819 .slice => {},
34820 .one => return sema.pointerDeref(block, src, slice_val, slice_ty),
34821 .many, .c => unreachable,
34822 }
34823 const slice = switch (zcu.intern_pool.indexToKey(slice_val.toIntern())) {
34824 .undef => return sema.failWithUseOfUndef(block, src, null),
34825 .slice => |slice| slice,
34826 else => unreachable,
34827 };
34828 if (slice.len == .undef_usize) return sema.failWithUndefSliceLen(block, src);
34829 const casted_ptr = try pt.sliceToArrayPtr(slice);
34830 return sema.pointerDeref(block, src, casted_ptr, casted_ptr.typeOf(zcu));
34831}
34832
34833fn analyzeUnreachable(sema: *Sema, block: *Block, src: LazySrcLoc, safety_check: bool) !void {
34834 if (safety_check and block.wantSafety()) {
34835 // We only apply the first hint in a branch.
34836 // This allows user-provided hints to override implicit cold hints.
34837 if (sema.branch_hint == null) {
34838 sema.branch_hint = .cold;
34839 }
34840
34841 try sema.safetyPanic(block, src, .reached_unreachable);
34842 } else {
34843 _ = try block.addNoOp(.unreach);
34844 }
34845}
34846
34847/// This should be called exactly once, at the end of a `Sema`'s lifetime.
34848/// It takes the exports stored in `sema.export` and flushes them to the `Zcu`
34849/// to be processed by the linker after the update.
34850pub fn flushExports(sema: *Sema) !void {
34851 if (sema.exports.items.len == 0) return;
34852
34853 const zcu = sema.pt.zcu;
34854 const gpa = zcu.gpa;
34855
34856 assert(!zcu.single_exports.contains(sema.owner));
34857 assert(!zcu.multi_exports.contains(sema.owner));
34858
34859 if (sema.exports.items.len == 1) {
34860 try zcu.single_exports.ensureUnusedCapacity(gpa, 1);
34861 const export_idx: Zcu.Export.Index = zcu.free_exports.pop() orelse idx: {
34862 _ = try zcu.all_exports.addOne(gpa);
34863 break :idx @fromBackingInt(@intCast(zcu.all_exports.items.len - 1));
34864 };
34865 export_idx.ptr(zcu).* = sema.exports.items[0];
34866 zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx);
34867 } else {
34868 try zcu.multi_exports.ensureUnusedCapacity(gpa, 1);
34869 const exports_base = zcu.all_exports.items.len;
34870 try zcu.all_exports.appendSlice(gpa, sema.exports.items);
34871 zcu.multi_exports.putAssumeCapacityNoClobber(sema.owner, .{
34872 .index = @intCast(exports_base),
34873 .len = @intCast(sema.exports.items.len),
34874 });
34875 }
34876}
34877
34878pub const castMemory = @import("Sema/reinterpret.zig").castMemory;
34879pub const spliceMemory = @import("Sema/reinterpret.zig").spliceMemory;
34880
34881const loadComptimePtr = @import("Sema/comptime_ptr_access.zig").loadComptimePtr;
34882const ComptimeLoadResult = @import("Sema/comptime_ptr_access.zig").ComptimeLoadResult;
34883const storeComptimePtr = @import("Sema/comptime_ptr_access.zig").storeComptimePtr;
34884const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStoreResult;
34885
34886pub const type_resolution = @import("Sema/type_resolution.zig");
34887pub const ensureLayoutResolved = type_resolution.ensureLayoutResolved;
34888pub const ensureStructDefaultsResolved = type_resolution.ensureStructDefaultsResolved;
34889
34890pub fn getStdLangType(sema: *Sema, src: LazySrcLoc, decl: Zcu.StdLangDecl) SemaError!Type {
34891 assert(decl.kind() == .type);
34892 try sema.ensureMemoizedStateResolved(src, decl.stage());
34893 return .fromInterned(sema.pt.zcu.std_lang_decl_values.get(decl));
34894}
34895pub fn getStdLangValue(sema: *Sema, src: LazySrcLoc, decl: Zcu.StdLangDecl) SemaError!InternPool.Index {
34896 assert(decl.kind() != .type);
34897 try sema.ensureMemoizedStateResolved(src, decl.stage());
34898 return sema.pt.zcu.std_lang_decl_values.get(decl);
34899}
34900
34901pub const NavPtrModifiers = struct {
34902 @"align": Alignment,
34903 @"linksection": InternPool.OptionalNullTerminatedString,
34904 @"addrspace": std.lang.AddressSpace,
34905};
34906
34907pub fn resolveNavPtrModifiers(
34908 sema: *Sema,
34909 block: *Block,
34910 zir_decl: Zir.Inst.Declaration.Unwrapped,
34911 decl_inst: Zir.Inst.Index,
34912 nav_ty: Type,
34913) CompileError!NavPtrModifiers {
34914 const pt = sema.pt;
34915 const zcu = pt.zcu;
34916 const comp = zcu.comp;
34917 const gpa = comp.gpa;
34918 const io = comp.io;
34919 const ip = &zcu.intern_pool;
34920
34921 const align_src = block.src(.{ .node_offset_var_decl_align = .zero });
34922 const section_src = block.src(.{ .node_offset_var_decl_section = .zero });
34923 const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = .zero });
34924
34925 const @"align": InternPool.Alignment = a: {
34926 const align_body = zir_decl.align_body orelse break :a .none;
34927 const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst);
34928 break :a try sema.analyzeAsAlign(block, align_src, align_ref);
34929 };
34930
34931 const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
34932 const linksection_body = zir_decl.linksection_body orelse break :ls .none;
34933 const linksection_ref = try sema.resolveInlineBody(block, linksection_body, decl_inst);
34934 const bytes = try sema.toConstString(block, section_src, linksection_ref, .{ .simple = .@"linksection" });
34935 if (std.mem.findScalar(u8, bytes, 0) != null) {
34936 return sema.fail(block, section_src, "linksection cannot contain null bytes", .{});
34937 } else if (bytes.len == 0) {
34938 return sema.fail(block, section_src, "linksection cannot be empty", .{});
34939 }
34940 break :ls try ip.getOrPutStringOpt(gpa, io, pt.tid, bytes, .no_embedded_nulls);
34941 };
34942
34943 const @"addrspace": std.lang.AddressSpace = as: {
34944 const addrspace_ctx: std.Target.AddressSpaceContext = switch (zir_decl.kind) {
34945 .@"var" => .variable,
34946 else => switch (nav_ty.zigTypeTag(zcu)) {
34947 .@"fn" => .function,
34948 else => .constant,
34949 },
34950 };
34951 const target = zcu.getTarget();
34952 const addrspace_body = zir_decl.addrspace_body orelse {
34953 if (zir_decl.linkage == .@"extern" and
34954 target.cpu.arch.isSpirV() and
34955 nav_ty.zigTypeTag(zcu) != .@"fn")
34956 {
34957 return sema.fail(
34958 block,
34959 block.src(.{ .node_offset_var_decl_ty = .zero }),
34960 "SPIR-V extern variables require an explicit address space",
34961 .{},
34962 );
34963 }
34964 break :as switch (addrspace_ctx) {
34965 .function => target_util.defaultAddressSpace(target, .function),
34966 .variable => target_util.defaultAddressSpace(target, .global_mutable),
34967 .constant => target_util.defaultAddressSpace(target, .global_constant),
34968 else => unreachable,
34969 };
34970 };
34971 const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst);
34972 break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx);
34973 };
34974
34975 return .{
34976 .@"align" = @"align",
34977 .@"linksection" = @"linksection",
34978 .@"addrspace" = @"addrspace",
34979 };
34980}
34981
34982pub fn analyzeMemoizedState(sema: *Sema, stage: InternPool.MemoizedStateStage) CompileError!bool {
34983 const pt = sema.pt;
34984 const zcu = pt.zcu;
34985 const comp = zcu.comp;
34986 const gpa = comp.gpa;
34987 const io = comp.io;
34988 const ip = &zcu.intern_pool;
34989
34990 // This `Block` acts kind of like it's evaluating a `comptime` declaration in the root source
34991 // file of the standard library. In particular, its namespace is the root std namespace.
34992 var block: Block = block: {
34993 // Get the main struct type of the root source file of `std`. No need for a reference entry
34994 // because `std` is always an analysis root.
34995 const std_file_index = zcu.module_roots.get(zcu.std_mod).?.unwrap().?;
34996 try sema.declareDependency(.{ .source_file = std_file_index });
34997 try pt.ensureFilePopulated(std_file_index);
34998 const std_type: Type = .fromInterned(zcu.fileRootType(std_file_index));
34999 break :block .{
35000 .parent = null,
35001 .sema = sema,
35002 .namespace = std_type.getNamespaceIndex(zcu),
35003 .instructions = .empty,
35004 .inlining = null,
35005 .comptime_reason = null,
35006 .src_base_inst = std_type.typeDeclInst(zcu).?,
35007 .type_name_ctx = .empty,
35008 .type_fqn_ctx = .empty,
35009 };
35010 };
35011 defer block.instructions.deinit(gpa);
35012
35013 const std_lang_ty: Type = ty: {
35014 const std_src = block.nodeOffset(.zero);
35015 const decl_name = try ip.getOrPutString(gpa, io, pt.tid, "lang", .no_embedded_nulls);
35016 const nav = try sema.namespaceLookup(&block, std_src, block.namespace, decl_name) orelse {
35017 return sema.fail(&block, std_src, "'std' missing 'lang'", .{});
35018 };
35019 const uncoerced_val = try sema.analyzeNavVal(&block, std_src, nav);
35020 const decl_src: LazySrcLoc = .{
35021 .base_node_inst = ip.getNav(nav).srcInst(ip),
35022 .offset = .nodeOffset(.zero),
35023 };
35024 break :ty try sema.analyzeAsType(&block, decl_src, .std_lang_decl, uncoerced_val);
35025 };
35026
35027 var any_changed = false;
35028
35029 inline for (comptime std.enums.values(Zcu.StdLangDecl)) |std_lang_decl| {
35030 if (stage == comptime std_lang_decl.stage()) {
35031 const parent_ns_ty: Type, const parent_name: []const u8, const name: []const u8 = switch (comptime std_lang_decl.access()) {
35032 .direct => |name| .{ std_lang_ty, "std.lang", name },
35033 .nested => |nested| access: {
35034 const parent_decl, const name = nested;
35035 const parent_ty: Type = .fromInterned(zcu.std_lang_decl_values.get(parent_decl));
35036 break :access .{ parent_ty, "std.lang." ++ @tagName(parent_decl), name };
35037 },
35038 };
35039
35040 const parent_ns = parent_ns_ty.getNamespace(zcu).unwrap() orelse {
35041 return sema.fail(&block, block.nodeOffset(.zero), "'{s}' is not a container type", .{parent_name});
35042 };
35043 const parent_ty_src = parent_ns_ty.srcLoc(zcu);
35044 const name_nts = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls);
35045 const nav = try sema.namespaceLookup(&block, parent_ty_src, parent_ns, name_nts) orelse {
35046 return sema.fail(&block, parent_ty_src, "'{s}' missing '{s}'", .{ parent_name, name });
35047 };
35048 const uncoerced_val = try sema.analyzeNavVal(&block, parent_ty_src, nav);
35049
35050 const decl_src: LazySrcLoc = .{
35051 .base_node_inst = ip.getNav(nav).srcInst(ip),
35052 .offset = .nodeOffset(.zero),
35053 };
35054
35055 const val: Value = switch (std_lang_decl.kind()) {
35056 .type => val: {
35057 const ty = try sema.analyzeAsType(&block, decl_src, .std_lang_decl, uncoerced_val);
35058 try sema.ensureLayoutResolved(ty, decl_src, .std_lang_type);
35059 break :val ty.toValue();
35060 },
35061 .func => val: {
35062 const func_ty = try sema.getExpectedBuiltinFnType(std_lang_decl);
35063 const coerced = try sema.coerce(&block, func_ty, uncoerced_val, decl_src);
35064 break :val try sema.resolveConstDefinedValue(&block, decl_src, coerced, .{ .simple = .std_lang_decl });
35065 },
35066 .string => val: {
35067 const coerced = try sema.coerce(&block, .slice_const_u8, uncoerced_val, decl_src);
35068 break :val try sema.resolveConstDefinedValue(&block, decl_src, coerced, .{ .simple = .std_lang_decl });
35069 },
35070 };
35071
35072 if (zcu.std_lang_decl_values.get(std_lang_decl) != val.toIntern()) {
35073 zcu.std_lang_decl_values.set(std_lang_decl, val.toIntern());
35074 any_changed = true;
35075 }
35076 }
35077 }
35078
35079 return any_changed;
35080}
35081
35082/// Given that `decl.kind() == .func`, get the type expected of the function.
35083fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.StdLangDecl) CompileError!Type {
35084 const pt = sema.pt;
35085 return switch (decl) {
35086 // `noinline fn () void`
35087 .returnError => try pt.funcType(.{
35088 .param_types = &.{},
35089 .return_type = .void_type,
35090 .is_noinline = true,
35091 }),
35092
35093 // `fn ([]const u8, ?usize) noreturn`
35094 .@"panic.call" => try pt.funcType(.{
35095 .param_types = &.{
35096 .slice_const_u8_type,
35097 (try pt.optionalType(.usize_type)).toIntern(),
35098 },
35099 .return_type = .noreturn_type,
35100 }),
35101
35102 // `fn (anytype, anytype) noreturn`
35103 .@"panic.sentinelMismatch",
35104 .@"panic.inactiveUnionField",
35105 => try pt.funcType(.{
35106 .param_types = &.{ .generic_poison_type, .generic_poison_type },
35107 .return_type = .noreturn_type,
35108 }),
35109
35110 // `fn (anyerror) noreturn`
35111 .@"panic.unwrapError",
35112 .@"panic.unexpectedErrorCode",
35113 => try pt.funcType(.{
35114 .param_types = &.{.anyerror_type},
35115 .return_type = .noreturn_type,
35116 }),
35117
35118 // `fn (usize) noreturn`
35119 .@"panic.sliceCastLenRemainder" => try pt.funcType(.{
35120 .param_types = &.{.usize_type},
35121 .return_type = .noreturn_type,
35122 }),
35123
35124 // `fn (usize, usize) noreturn`
35125 .@"panic.outOfBounds",
35126 .@"panic.startGreaterThanEnd",
35127 => try pt.funcType(.{
35128 .param_types = &.{ .usize_type, .usize_type },
35129 .return_type = .noreturn_type,
35130 }),
35131
35132 // `fn () noreturn`
35133 .@"panic.reachedUnreachable",
35134 .@"panic.unwrapNull",
35135 .@"panic.castToNull",
35136 .@"panic.incorrectAlignment",
35137 .@"panic.invalidErrorCode",
35138 .@"panic.integerOutOfBounds",
35139 .@"panic.integerOverflow",
35140 .@"panic.shlOverflow",
35141 .@"panic.shrOverflow",
35142 .@"panic.divideByZero",
35143 .@"panic.exactDivisionRemainder",
35144 .@"panic.integerPartOutOfBounds",
35145 .@"panic.corruptSwitch",
35146 .@"panic.shiftRhsTooBig",
35147 .@"panic.invalidEnumValue",
35148 .@"panic.forLenMismatch",
35149 .@"panic.copyLenMismatch",
35150 .@"panic.memcpyAlias",
35151 .@"panic.noreturnReturned",
35152 .@"panic.loadUninstantiableType",
35153 => try pt.funcType(.{
35154 .param_types = &.{},
35155 .return_type = .noreturn_type,
35156 }),
35157
35158 .StackTrace,
35159 .CallingConvention,
35160 .SourceLocation,
35161 .Signedness,
35162 .AddressSpace,
35163 .VaList,
35164 .CallModifier,
35165 .AtomicOrder,
35166 .AtomicRmwOp,
35167 .ReduceOp,
35168 .FloatMode,
35169 .PrefetchOptions,
35170 .ExportOptions,
35171 .ExternOptions,
35172 .BranchHint,
35173 .assembly,
35174 .@"assembly.Clobbers",
35175 .Type,
35176 .@"Type.Fn",
35177 .@"Type.Fn.ParamAttributes",
35178 .@"Type.Fn.Attributes",
35179 .@"Type.Int",
35180 .@"Type.Float",
35181 .@"Type.Pointer",
35182 .@"Type.Pointer.Size",
35183 .@"Type.Pointer.Attributes",
35184 .@"Type.Array",
35185 .@"Type.Vector",
35186 .@"Type.Optional",
35187 .@"Type.ErrorUnion",
35188 .@"Type.ErrorSet",
35189 .@"Type.Enum",
35190 .@"Type.Enum.Mode",
35191 .@"Type.Union",
35192 .@"Type.Union.FieldAttributes",
35193 .@"Type.Struct",
35194 .@"Type.Struct.FieldAttributes",
35195 .@"Type.ContainerLayout",
35196 .@"Type.Opaque",
35197 .@"Type.Spirv",
35198 .@"Type.Spirv.Image",
35199 .@"Type.Spirv.Image.Usage",
35200 .@"Type.Spirv.Image.Format",
35201 .@"Type.Spirv.Image.Dimensionality",
35202 .@"Type.Spirv.Image.Depth",
35203 .@"Type.Spirv.Image.Access",
35204 .panic,
35205 => unreachable, // not a function (`decl.kind() != .func`)
35206 };
35207}
35208
35209pub fn setTypeName(
35210 sema: *Sema,
35211 block: *Block,
35212 wip: *const InternPool.WipContainerType,
35213 name_strategy: Zir.Inst.NameStrategy,
35214 anon_prefix: []const u8,
35215 inst: Zir.Inst.Index,
35216) CompileError!void {
35217 const pt = sema.pt;
35218 const zcu = pt.zcu;
35219 const comp = zcu.comp;
35220 const gpa = comp.gpa;
35221 const io = comp.io;
35222 const ip = &zcu.intern_pool;
35223
35224 strat: switch (name_strategy) {
35225 .anon => {
35226 // It would be neat to have "struct:line:column" but this name has
35227 // to survive incremental updates, where it may have been shifted down
35228 // or up to a different line, but unchanged, and thus not unnecessarily
35229 // semantically analyzed.
35230 // TODO: that would be possible, by detecting line number changes and renaming
35231 // types appropriately. However, `@typeName` becomes a problem then. If we remove
35232 // that builtin from the language, we can consider this.
35233 wip.setName(ip, try ip.getOrPutStringFmt(
35234 gpa,
35235 io,
35236 pt.tid,
35237 "{f}__{s}_{d}",
35238 .{ block.type_name_ctx.fmt(ip), anon_prefix, zcu.anon_name_counter },
35239 .no_embedded_nulls,
35240 ), try ip.getOrPutStringFmt(
35241 gpa,
35242 io,
35243 pt.tid,
35244 "{f}__{s}_{d}",
35245 .{ block.type_fqn_ctx.fmt(ip), anon_prefix, zcu.anon_name_counter },
35246 .no_embedded_nulls,
35247 ), .none);
35248 zcu.anon_name_counter += 1;
35249 },
35250 .parent => wip.setName(ip, block.type_name_ctx, block.type_fqn_ctx, sema.owner.unwrap().nav_val.toOptional()),
35251 .func => {
35252 const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse {
35253 return sema.failTransitive(.{ .lost_tracking = ip.funcZirBodyInst(sema.func_index) });
35254 });
35255 const zir_tags = sema.code.instructions.items(.tag);
35256
35257 var aw: std.Io.Writer.Allocating = .init(gpa);
35258 defer aw.deinit();
35259 const w = &aw.writer;
35260 w.writeByte('(') catch return error.OutOfMemory;
35261
35262 var arg_i: usize = 0;
35263 for (fn_info.param_body) |zir_inst| switch (zir_tags[@backingInt(zir_inst)]) {
35264 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
35265 const arg = sema.inst_map.get(zir_inst).?;
35266 // If this is being called in a generic function then analyzeCall will
35267 // have already resolved the args and this will work.
35268 // If not then this is a struct type being returned from a non-generic
35269 // function and the name doesn't matter since it will later
35270 // result in a compile error.
35271 const arg_val = sema.resolveValue(arg) orelse {
35272 continue :strat .anon;
35273 };
35274
35275 if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory;
35276
35277 // Limiting the depth here helps avoid type names getting too long, which
35278 // in turn helps to avoid unreasonably long symbol names for namespaced
35279 // symbols. Such names should ideally be human-readable, and additionally,
35280 // some tooling may not support very long symbol names.
35281 w.print("{f}", .{Value.fmtValueSemaFull(.{
35282 .val = arg_val,
35283 .pt = pt,
35284 .opt_sema = sema,
35285 .depth = 1,
35286 })}) catch return error.OutOfMemory;
35287
35288 arg_i += 1;
35289 continue;
35290 },
35291 else => continue,
35292 };
35293
35294 w.writeByte(')') catch return error.OutOfMemory;
35295 wip.setName(ip, try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}{s}", .{
35296 block.type_name_ctx.fmt(ip),
35297 aw.written(),
35298 }, .no_embedded_nulls), try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}{s}", .{
35299 block.type_fqn_ctx.fmt(ip),
35300 aw.written(),
35301 }, .no_embedded_nulls), .none);
35302 },
35303 .dbg_var => {
35304 // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions.
35305 const ref = inst.toRef();
35306 const zir_tags = sema.code.instructions.items(.tag);
35307 const zir_data = sema.code.instructions.items(.data);
35308 const var_name = for (@backingInt(inst)..zir_tags.len) |i| switch (zir_tags[i]) {
35309 .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) {
35310 break zir_data[i].str_op.getStr(sema.code);
35311 },
35312 else => {},
35313 } else {
35314 continue :strat .anon;
35315 };
35316 wip.setName(ip, try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{
35317 // this "{f}." should be elided, but there's currently no way to get the parent function
35318 block.type_name_ctx.fmt(ip), var_name,
35319 }, .no_embedded_nulls), try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{
35320 block.type_fqn_ctx.fmt(ip), var_name,
35321 }, .no_embedded_nulls), .none);
35322 },
35323 }
35324}
35325
35326fn zirStructDecl(
35327 sema: *Sema,
35328 block: *Block,
35329 inst: Zir.Inst.Index,
35330) CompileError!Air.Inst.Ref {
35331 const pt = sema.pt;
35332 const zcu = pt.zcu;
35333 const comp = zcu.comp;
35334 const gpa = comp.gpa;
35335 const io = comp.io;
35336 const ip = &zcu.intern_pool;
35337
35338 const tracked_inst = try block.trackZir(inst);
35339
35340 const src: LazySrcLoc = .{
35341 .base_node_inst = tracked_inst,
35342 .offset = .nodeOffset(.zero),
35343 };
35344
35345 const struct_decl = sema.code.getStructDecl(inst);
35346
35347 const captures = try sema.getCaptures(block, src, struct_decl.captures, struct_decl.capture_names);
35348
35349 const ty: Type = switch (try ip.getDeclaredStructType(gpa, io, pt.tid, .{
35350 .zir_index = tracked_inst,
35351 .captures = captures,
35352 .fields_len = @intCast(struct_decl.field_names.len),
35353 .layout = struct_decl.layout,
35354 .any_comptime_fields = struct_decl.field_comptime_bits != null,
35355 .any_field_defaults = struct_decl.field_default_body_lens != null,
35356 .any_field_aligns = struct_decl.field_align_body_lens != null,
35357 .packed_backing_mode = if (struct_decl.backing_int_type_body != null) .explicit else .auto,
35358 })) {
35359 .existing => |ty| .fromInterned(ty),
35360 .wip => |wip| ty: {
35361 errdefer wip.cancel(ip, pt.tid);
35362 try sema.setTypeName(block, &wip, struct_decl.name_strategy, "struct", inst);
35363 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
35364 .parent = block.namespace.toOptional(),
35365 .owner_type = wip.index,
35366 .file_scope = block.getFileScopeIndex(zcu),
35367 .generation = zcu.generation,
35368 });
35369 errdefer pt.destroyNamespace(new_namespace_index);
35370 try pt.scanNamespace(new_namespace_index, struct_decl.decls);
35371 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
35372 break :ty .fromInterned(wip.finish(ip, new_namespace_index));
35373 },
35374 };
35375
35376 try sema.addTypeReferenceEntry(src, ty);
35377 pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)) catch |err| switch (err) {
35378 error.LostZirContainerDecl => unreachable, // we literally just tracked it
35379 else => |e| return e,
35380 };
35381
35382 return .fromType(ty);
35383}
35384fn zirUnionDecl(
35385 sema: *Sema,
35386 block: *Block,
35387 inst: Zir.Inst.Index,
35388) CompileError!Air.Inst.Ref {
35389 const pt = sema.pt;
35390 const zcu = pt.zcu;
35391 const comp = zcu.comp;
35392 const gpa = comp.gpa;
35393 const io = comp.io;
35394 const ip = &zcu.intern_pool;
35395
35396 const tracked_inst = try block.trackZir(inst);
35397
35398 const src: LazySrcLoc = .{
35399 .base_node_inst = tracked_inst,
35400 .offset = .nodeOffset(.zero),
35401 };
35402
35403 const union_decl = sema.code.getUnionDecl(inst);
35404
35405 const captures = try sema.getCaptures(block, src, union_decl.captures, union_decl.capture_names);
35406
35407 const ty: Type = switch (try ip.getDeclaredUnionType(gpa, io, pt.tid, .{
35408 .zir_index = tracked_inst,
35409 .captures = captures,
35410 .fields_len = @intCast(union_decl.field_names.len),
35411 .layout = union_decl.kind.layout(),
35412 .any_field_aligns = union_decl.field_align_body_lens != null,
35413 .tag_usage = switch (union_decl.kind) {
35414 .auto => if (block.wantSafeTypes()) .safety else .none,
35415
35416 .tagged_explicit,
35417 .tagged_enum,
35418 .tagged_enum_explicit,
35419 => .tagged,
35420
35421 .@"extern",
35422 .@"packed",
35423 .packed_explicit,
35424 => .none,
35425 },
35426 .enum_tag_mode = switch (union_decl.kind) {
35427 .tagged_explicit => .explicit,
35428 else => .auto,
35429 },
35430 .packed_backing_mode = switch (union_decl.kind) {
35431 .packed_explicit => .explicit,
35432 else => .auto,
35433 },
35434 })) {
35435 .existing => |ty| .fromInterned(ty),
35436 .wip => |wip| ty: {
35437 errdefer wip.cancel(ip, pt.tid);
35438 try sema.setTypeName(block, &wip, union_decl.name_strategy, "union", inst);
35439 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
35440 .parent = block.namespace.toOptional(),
35441 .owner_type = wip.index,
35442 .file_scope = block.getFileScopeIndex(zcu),
35443 .generation = zcu.generation,
35444 });
35445 errdefer pt.destroyNamespace(new_namespace_index);
35446 try pt.scanNamespace(new_namespace_index, union_decl.decls);
35447 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
35448 break :ty .fromInterned(wip.finish(ip, new_namespace_index));
35449 },
35450 };
35451
35452 try sema.addTypeReferenceEntry(src, ty);
35453 pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)) catch |err| switch (err) {
35454 error.LostZirContainerDecl => unreachable, // we literally just tracked it
35455 else => |e| return e,
35456 };
35457
35458 return .fromType(ty);
35459}
35460fn zirEnumDecl(
35461 sema: *Sema,
35462 block: *Block,
35463 inst: Zir.Inst.Index,
35464) CompileError!Air.Inst.Ref {
35465 const pt = sema.pt;
35466 const zcu = pt.zcu;
35467 const comp = zcu.comp;
35468 const gpa = comp.gpa;
35469 const io = comp.io;
35470 const ip = &zcu.intern_pool;
35471
35472 const tracked_inst = try block.trackZir(inst);
35473
35474 const src: LazySrcLoc = .{
35475 .base_node_inst = tracked_inst,
35476 .offset = .nodeOffset(.zero),
35477 };
35478
35479 const enum_decl = sema.code.getEnumDecl(inst);
35480
35481 const captures = try sema.getCaptures(block, src, enum_decl.captures, enum_decl.capture_names);
35482
35483 const ty: Type = switch (try ip.getDeclaredEnumType(gpa, io, pt.tid, .{
35484 .zir_index = tracked_inst,
35485 .captures = captures,
35486 .fields_len = @intCast(enum_decl.field_names.len),
35487 .nonexhaustive = enum_decl.nonexhaustive,
35488 .int_tag_mode = if (enum_decl.tag_type_body != null) .explicit else .auto,
35489 })) {
35490 .existing => |ty| .fromInterned(ty),
35491 .wip => |wip| ty: {
35492 errdefer wip.cancel(ip, pt.tid);
35493 try sema.setTypeName(block, &wip, enum_decl.name_strategy, "enum", inst);
35494 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
35495 .parent = block.namespace.toOptional(),
35496 .owner_type = wip.index,
35497 .file_scope = block.getFileScopeIndex(zcu),
35498 .generation = zcu.generation,
35499 });
35500 errdefer pt.destroyNamespace(new_namespace_index);
35501 try pt.scanNamespace(new_namespace_index, enum_decl.decls);
35502 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
35503 break :ty .fromInterned(wip.finish(ip, new_namespace_index));
35504 },
35505 };
35506
35507 try sema.addTypeReferenceEntry(src, ty);
35508 pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)) catch |err| switch (err) {
35509 error.LostZirContainerDecl => unreachable, // we literally just tracked it
35510 else => |e| return e,
35511 };
35512
35513 return .fromType(ty);
35514}
35515fn zirOpaqueDecl(
35516 sema: *Sema,
35517 block: *Block,
35518 inst: Zir.Inst.Index,
35519) CompileError!Air.Inst.Ref {
35520 const pt = sema.pt;
35521 const zcu = pt.zcu;
35522 const comp = zcu.comp;
35523 const gpa = comp.gpa;
35524 const io = comp.io;
35525 const ip = &zcu.intern_pool;
35526
35527 const tracked_inst = try block.trackZir(inst);
35528
35529 const src: LazySrcLoc = .{
35530 .base_node_inst = tracked_inst,
35531 .offset = .nodeOffset(.zero),
35532 };
35533
35534 const opaque_decl = sema.code.getOpaqueDecl(inst);
35535
35536 const captures = try sema.getCaptures(block, src, opaque_decl.captures, opaque_decl.capture_names);
35537
35538 const ty: Type = switch (try ip.getDeclaredOpaqueType(gpa, io, pt.tid, .{
35539 .zir_index = tracked_inst,
35540 .captures = captures,
35541 })) {
35542 .existing => |ty| .fromInterned(ty),
35543 .wip => |wip| ty: {
35544 errdefer wip.cancel(ip, pt.tid);
35545 try sema.setTypeName(block, &wip, opaque_decl.name_strategy, "opaque", inst);
35546 const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{
35547 .parent = block.namespace.toOptional(),
35548 .owner_type = wip.index,
35549 .file_scope = block.getFileScopeIndex(zcu),
35550 .generation = zcu.generation,
35551 });
35552 errdefer pt.destroyNamespace(new_namespace_index);
35553 try pt.scanNamespace(new_namespace_index, opaque_decl.decls);
35554 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index);
35555 break :ty .fromInterned(wip.finish(ip, new_namespace_index));
35556 },
35557 };
35558
35559 try sema.addTypeReferenceEntry(src, ty);
35560 pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)) catch |err| switch (err) {
35561 error.LostZirContainerDecl => unreachable, // we literally just tracked it
35562 else => |e| return e,
35563 };
35564
35565 return .fromType(ty);
35566}
35567
35568/// Registers an error indicating a dependency loop: we have introduced a dependency on `want` (with
35569/// reason `want_reason`) but have learnt that `want` is already in `zcu.analysis_in_progress`.
35570pub fn failWithDependencyLoop(
35571 sema: *Sema,
35572 want: AnalUnit,
35573 want_reason: *const Zcu.DependencyReason,
35574) SemaError {
35575 const pt = sema.pt;
35576 const zcu = pt.zcu;
35577 const gpa = zcu.comp.gpa;
35578
35579 const in_progress_len = zcu.analysis_in_progress.count();
35580 var index = zcu.analysis_in_progress.getIndex(want).? + 1;
35581
35582 try zcu.dependency_loops.ensureUnusedCapacity(gpa, 1);
35583 try zcu.dependency_loop_nodes.ensureUnusedCapacity(gpa, in_progress_len - index + 1);
35584
35585 zcu.dependency_loops.putAssumeCapacityNoClobber(want, {});
35586
35587 while (index <= in_progress_len) : (index += 1) {
35588 const parent_unit = zcu.analysis_in_progress.keys()[index - 1];
35589 const unit, const reason = if (index == in_progress_len) .{
35590 want,
35591 want_reason,
35592 } else .{
35593 zcu.analysis_in_progress.keys()[index],
35594 zcu.analysis_in_progress.values()[index],
35595 };
35596
35597 zcu.dependency_loop_nodes.putAssumeCapacityNoClobber(parent_unit, .{
35598 .unit = unit,
35599 .reason = reason.?.*,
35600 });
35601 }
35602
35603 // A dependency loop error will be reported. Mark us all as transitive failures.
35604 return sema.failTransitive(.dependency_loop);
35605}
35606
35607/// Marks the owner of `sema` as having failed semantic failed *without* an error message, and
35608/// returns failure. This function is suitable to call when any one of the following is true:
35609///
35610/// * `sema.owner` is guaranteed to be unreferenced on this update, for instance because it uses a
35611/// dead `InternPool.TrackedInst`.
35612///
35613/// * There is guaranteed to be a compile error if this unit is referenced. In practice, this means
35614/// that either there is an error elsewhere in the pipeline (e.g. AstGen), or we depend on another
35615/// `AnalUnit` which has itself failed.
35616pub fn failTransitive(sema: *Sema, reason: Zcu.TransitiveFailureReason) SemaError {
35617 assert(sema.err == null);
35618 const zcu = sema.pt.zcu;
35619 const unit = sema.owner;
35620
35621 log.debug("transitive failure analyzing '{f}' ({t})", .{ zcu.fmtAnalUnit(unit), reason });
35622
35623 assert(!zcu.failed_analysis.contains(unit));
35624 try zcu.transitive_failed_analysis.putNoClobber(
35625 zcu.comp.gpa,
35626 unit,
35627 if (build_options.enable_debug_extensions) reason,
35628 );
35629
35630 return error.AlreadyReported;
35631}